• C# 图解教程 第5版 —— 第6章 方法


    6.1 方法的结构

    • 返回的数据类型。
    • 方法名称。
    • 参数列表。
    image-20231015165612625
    图6.1 方法的结构

    6.2 方法体内部的代码执行

    ​ 方法体是一个块,可以包含以下内容:

    • 局部变量;
    • 控制流结构;
    • 方法调用;
    • 内嵌的块;
    • 其他方法(局部函数)。

    6.3 局部变量

    • 局部变量的生存周期仅限于创建它的块内。
      • 声明时开始存在。
      • 块尾结束存在。
    • 可以在方法体内部任意位置声明,声明后才能使用。
    image-20231015165958072
    图6.2 对比实例字段和局部变量

    6.3.1 类型推断和 var 关键字

    ​ 使用 var 关键字可以进行类型推断,而不需要明确指定变量类型。var 关键字并不改变 C# 的强类型性质。

    • 只能用于局部变量,不能用于字段。
    • 只能在变量声明中包含初始化时使用。
    • 一旦编译器推断出变量的类型,它就是固定且不能更改的。

    6.3.2 嵌套块中的局部变量

    ​ 在 C# 中不论嵌套级别如何,都不能在第一个名称的有效范围内声明另一个同名的局部变量。

    6.4 局部常量

    • 声明时必须初始化。
      • 初始化值必须在编译时就可以确定,通常为预定义简单类型或 null 引用。
    • 声明后不能改变。

    ​ 和局部变量一样,局部常量声明在方法体或代码块里,并在声明它的块结束的地方失效。

    ​ 注意:const 不是修饰符,而是核心声明的一部分。

    6.5 控制流

    • 选择语句
      • if
      • if … else
      • switch
    • 迭代语句
      • for
      • while
      • do
      • foreach
    • 跳转语句
      • break
      • continue
      • goto
      • return

    6.6 方法调用(*)

    6.7 返回值(*)

    6.8 返回语句和 void 方法

    6.9 局部函数

    ​ C# 7.0 开始,可以在一个方法中声明另一个单独的方法,称为局部函数。

    6.10 参数(*)

    6.11 值参数

    ​ 使用值参数时,会发生如下操作:

    1. 在栈中为形参分配空间。
    2. 将实参的值复制给形参。

    ​ 方法使用值参数不能改变原始的值类型数据,但是可以改变引用类型的数据:

    class MyClass { public int Val = 20; }
    
    class Program {
        static void MyMethod(MyClass f1, int f2) { // 形参
            f1.Val = f1.Val + 5;
            f2     = f2 + 5;
        }
        
        static void Main() {
            MyClass a1 = new MyClass();
            int a2 = 10;
            MyMethod(a1, a2); // 实参
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    image-20231015185433612
    图6.3 值参数

    6.12 引用参数

    • 在方法的声明和调用中都使用 ref 修饰符。
    • 实参必须是已经被赋值的变量,引用类型变量可以是 null。
    image-20231015185911962
    图6.4 ref 使用说明

    ​ 引用参数具有如下特征:

    • 不会在栈上为形参分配内存。
    • 形参的参数名将作为实参变量的别名,指向相同的内存位置。
    image-20231015190030549
    图6.5 对于引用参数,形参就像实参的别名

    6.13 引用类型作为值参数和引用参数

    ​ 对于引用类型对象:

    • 作为值参数传递:如果在方法内创建一个新对象并赋值给形参,对实参没有影响。
    • 作为引用参数传递:如果在方法内创建一个新对象并赋值给形参,则实参也会随之改变。
    image-20231015190519645
    图6.6 对用作值参数的引用类型对象赋值
    image-20231015190632315
    图6.7 为用作引用参数的引用类型对象赋值

    ​ 将引用类型对象作为引用参数传递,目的是改变引用对象;

    ​ 如果仅需改变引用类型对象的内容,只需值参数传递即可。

    6.14 输出参数

    • 在方法的声明和调用中都使用 out 修饰符。
    • 实参必须是变量,使用前可以不赋值。
    • 形参的参数名也作为实参变量的别名,指向相同的内存位置。
    image-20231015190939239
    图6.8 out 使用说明

    ​ 与 ref 不同,out 有如下要求:

    1. 给输出参数赋值后才能读取。
    2. 方法返回之前,必须给输出参数赋值。

    ​ C# 7.0 后,可以对输出参数进行简化声明,不需要预先声明一个变量来用作 out 参数了。如图 6.9 所示,声明后的 a1 和 a2 可以在方法调用结束后继续使用。

    image-20231015191333370
    图6.9 out 新的声明方式

    6.15 参数数组

    • 在形参前使用 params 修饰符,并在数据类型后放置一组方括号。
    • 参数列表中只能有一个参数数组,且必须为参数列表的最后一个。
    • 参数数组中的所有参数类型必须相同。
    image-20231015191753290
    图6.10 params 使用说明

    6.15.1 方法调用

    ​ 可以使用两种方式为参数数组提供实参:

    1. 使用逗号分隔的元素列表。
    ListInts(10, 20, 30); // 3 个 int
    
    • 1
    1. 一个同类型的一维数组。
    int[] intArray = {1, 2, 3};
    ListInts(intArray);
    
    • 1
    • 2

    ​ 注意:在调用时不使用 params 修饰符。

    ​ 使用独立实参调用时,编译器将执行以下步骤:

    1. 接受实参列表,在堆中创建并初始化一个数组;
    2. 把数组的引用保存到栈中的形参里;
    3. 如果没有实参(个数为 0),则编译器会创建一个具有 0 个元素的数组来使用。
    void ListInts(params int[] inVals) { ... } // 方法声明
    
    ...
    ListInts();              // 0 个实参
    ListInts(1, 2, 3);       // 3 个实参
    ListInts(4, 5, 6, 7, 8); // 5 个实参
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ​ 当数组 inVals 在堆中被创建时,实参的值被赋值到数组中,因此可看做值参数:

    • 数组参数为值类型:值被复制,实参不受影响;
    • 数组参数为引用类型:引用被复制,实参引用的对象在内部会受到影响。
    image-20231015192755242
    图6.11 参数数组示例

    6.15.2 将数组作为实参

    ​ 编译器将使用传入的数组而不是重新创建一个。

    6.16 参数类型总结

    image-20231015192951213
    图6.12 参数类型语法使用总结

    6.17 ref 局部变量和 ref 返回

    ​ 创建别名的语法需要使用 ref 两次。

    image-20231015193051479
    图6.13 创建 ref 局部变量

    ​ ref 返回使得方法可以返回引用而不是值,同样也需要使用两次 ref:

    image-20231015193219646
    图6.14 ref 返回示例

    ​ 有关 ref 的使用有如下注意事项:

    1. ref return 不能返回如下内容:
      • 空值。
      • 常量。
      • 枚举成员。
      • 类或结构体的属性。
      • 指向只读位置的指针。
    2. ref return 不能返回方法内部的局部变量;
    3. ref 局部变量只能被赋值一次,后面出现的等号表示赋值;
    4. 如果调用 ref 返回方法时未使用 ref 关键字,则返回的是值而不是引用;
    5. 将 ref 局部变量作为常规的实际参数传递给其他方法时,传递的仍是 ref 指向的副本而不是引用。

    6.18 方法重载

    ​ 使用相同名称的方法必须和其他同名方法有不同的签名,签名由如下信息组成:

    • 方法名称。
    • 参数数目。
    • 参数的数据类型和顺序。
    • 参数修饰符。

    ​ 返回类型和形参名称都不是签名的一部分。

    image-20231015212331455
    图6.15 签名组成成分

    6.19 命名参数

    ​ C# 可以使用命名参数,显示指定参数的名称,就能够以任意顺序在方法中列出实参。

    image-20231015212314102
    图6.16 命名参数示例

    ​ 可以同时使用位置参数和命名参数,但所有位置参数必须先列出。

    image-20231015212232064
    图6.17 位置参数和命名参数同时使用

    6.20 可选参数

    ​ 可选参数能够设置参数的默认值,图 6.18 列出了哪些时候能使用可选参数。

    image-20231015212147383
    图6.18 可选参数只能是值参数类型

    ​ 所有必填参数需放在可选参数声明之前,params 参数放在可选参数之后,如图 6.19 所示。

    image-20231015212530555
    图6.19 所有参数声明顺序
    • 必须从可选参数列表的最后向前开始省略,而不是任意省略参数。
    • 如果需要任意省略参数,需要配合命名参数来实现以消除赋值的歧义。
    class MyClass {
        double GetCylinderVolume(double radius = 3.0, double height = 4.0) {
            return 3.1416 * radius * radius * height;
        }
        
        static void Main() {
            MyClass mc = new MyCalss();
            double volume;
            
            volume = mc.GetCylinderVolume(3.0, 4.0);    // 位置参数
            volume = mc.GetCylinderVolume(radius: 2.0); // 使用 height 默认值
            volume = mc.GetCylinderVolume(height: 2.0); // 使用 radius 默认值
            volume = mc.GetCylinderVolume();            // 使用两个默认值
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    6.21 栈帧

    ​ 调用方法时,内存从栈的顶部开始分配,保存和方法关联的一些数据项。这块内存称为方法的栈帧

    • 栈帧包含如下内容:
      • 返回地址,即方法返回时继续执行的位置。
      • 分配内存的参数,即值参数(参数数组,如果有的话)。
      • 和方法调用相关的其他管理数据项。
    • 在方法调用时,整个栈帧都会压入栈。
    • 方法退出时,整个栈帧会从栈上弹出,也称栈展开

    6.22 递归(*)

  • 相关阅读:
    堆的应用-----Top k 问题
    SRAM之ECC检测机制
    vue 修改 el-cascader 面板的样式
    【分享】xpath的属性表达式
    Nginx网络服务之监控模块
    ESP8266-Arduino编程实例-光敏(LDR)传感器驱动
    [附源码]计算机毕业设计药品仓库及预警管理系统Springboot程序
    短视频账号矩阵系统saas源码搭建/技术
    直播预告 | 构建业务智联,快速拥抱财务数字化转型
    【力扣-数据结构和算法-头哨兵】移除链表元素
  • 原文地址:https://blog.csdn.net/zheliku/article/details/133848250