C#8.0本质论第五章--方法和参数

C#8.0本质论第五章–方法和参数

5.1方法的调用

5.1.1命名空间

命名空间主要用于按功能领域组织类型,以便查找和理解这些类型。此外,命名空间还有助于防范类型名称冲突。

5.1.2类型名称
5.1.3作用域
5.1.4方法名称
5.1.5形参和实参
5.1.6方法返回值
5.1.7对比语句和方法调用

5.2方法的声明

C#不支持全局方法,一切都必须在类型声明中。这正是Main方法标记为static的原因。

5.2.1参数声明
5.2.2方法返回类型声明

如果方法又返回类型,它的主体必须没有“不可到达的结束点”。换言之,方法不能在不返回值的情况下碰到大括号而自然结束。

从C#7.0开始多个值可以通过元组语法打包成元组返回。

5.2.3表达式主体方法

为简化方法的定义,C#6.0引入了表达式主体方法,允许用表达式代替完整方法主体。

和C++不同C#类从来不将实现与声明分开(C#确实支持名为“分部方法”的高级功能,允许将方法的声明和实现分开)。

    static string GetFullName( string firstName, string lastName) =>
              $"{ firstName } { lastName }";

5.3using指令

using指令不“导入”任何嵌套命名空间中的类型,虽然添加了using System;,但要访问System.Text中的StringBuilder类型必须添加一个using System.Text指令或对类型进行完全限定。

在java中可以用通配符导入命名空间,但C#不允许,每个命名空间都必须显示导入。

5.3.1using static指令
5.3.2使用别名

可以利用using指令为命名空间或类型取一个别名。别名的两个最常见的用途是消除两个同名类型的歧义和缩写长名称。

5.4Main()的返回值和参数

C#支持在执行程序时提供命令行参数,并允许从Main()方法返回状态标识符。

5.5高级方法参数

5.5.1值参数

参数默认采用传值方式。

int a=123;
string b="456";
ClassC c=new ClassC();

func(a,b,c);

func(int a,string b,ClassC c)
{
    ...
}

上面的调用中,a传的是123这个数值,b传的是"456"这个字符串字面值,c传的是c指向的对象的引用(地址),也就是c这个变量的值。

这些值传进函数里后赋值给了函数里的a,b,c,所以函数里的abc是新创建的局部变量。给c的某个成员赋值会影响外面的c,因为c都指向同一个对象。但如果给c变量new一个新对象,不会对外面的c造成影响,因为c是局部变量。字符串同理。

5.5.2引用参数

调用者应初始化传引用的局部变量。

函数里的ref参数只是传递的变量的别名,换言之,引用参数的作用只是为现有变量分配参数名,而非创建新变量并将实参的值拷贝给它。

5.5.3输出参数

out参数功能上与ref参数完全一致,区别就是C#语言对别名变量的读写又不同规定。如参数被标记为out,编译器会核实在方法所有正常返回的代码路径中,是否都对该参数进行了赋值。

从C#7.0起可在调用方法前以内联的形式声明out变量,而不必在使用前声明out变量。

C#7.0的另一个功能是允许完全放弃out参数,可用下划线放弃参数:

TryGetPhoneButton(character,out _);

C#7.0写代码时,返回两个或更多值应首选元组语法。

5.5.4只读传引用

C#7.2支持以传引用的方式传入只读值类型。避免了每次调用方法都创建值类型的拷贝,而且不用担心值类型参数被修改。换言之,其作用是在传值时减少拷贝量,为参数添加in修饰符。

5.5.5返回引用

C#7.0新增的另一个功能返回对变量的引用。

// Returning a reference
public static ref byte FindFirstRedEyePixel(byte[] image)
{
    // Do fancy image detection perhaps with machine learning
    for (int counter = 0; counter < image.Length; counter++)
    {
        if (image[counter] == (byte)ConsoleColor.Red)
        {
            return ref image[counter];
        }
    }
    throw new InvalidOperationException("No pixels are red.");
}
public static void Main()
{
    byte[] image = new byte[254];
    // Load image
    int index = new Random().Next(0, image.Length - 1);
    image[index] =
        (byte)ConsoleColor.Red;
    Console.WriteLine(
        $"image[{index}]={(ConsoleColor)image[index]}");
    // ...
 
    // Obtain a reference to the first red pixel
    ref byte redPixel = ref FindFirstRedEyePixel(image);
    // Update it to be Black
    redPixel = (byte)ConsoleColor.Black;
    Console.WriteLine(
        $"image[{index}]={(ConsoleColor)image[redPixel]}");
}

ref局部变量被初始化为引用一个特定变量,以后不能修改为引用其他变量。(有点像C++里引用了)

5.5.6参数数组

要么以逗号分隔的字符串参数,要么是单个字符串数组,声明了参数数组之后,每个参数都作为参数数组的成员来访问。

参数数组必须是最后一个,所以只能有一个。

5.6递归

5.7方法重载

方法重载是一种**操作性多态(**operational polymorphism)。如由于数据变化造成同一个逻辑操作具有许多形态,就会发生多态。

5.8可选参数

C#4.0新增了对可选参数的支持。可选参数一点要放到所有必须参数后面。默认值必须是常量或者其他能在编译时确定的值。

C#4.0新增的另一个方法调用功能是具名参数

public static void Main()
{
    DisplayGreeting(
        firstName: "Inigo", lastName: "Montoya");
}
 
public static void DisplayGreeting(
    string firstName,
    string? middleName = null,
    string? lastName = null
    )
{
    // ...
}

如果一个方法有大量参数,许多都可选,那么具名参数语法肯定能带来不少遍历,但代价是牺牲了方法接口的灵活性,参数名变成了方法接口的一部分。

假如由于其中一个方法有可选参数使得两个方法都适用,编译器最终将选择无可选参数的方法。

5.9用异常实现基本错误处理

5.9.1捕捉错误
5.9.2使用throw语句报告错误