练习 - 使用值和引用类型参数

已完成

在 C# 中,变量可分为两种主要类型:值类型和引用类型。 这些类型描述变量如何存储其值。

值类型,例如intboolfloatdoublechar直接包含值。 引用类型(如stringarray)和对象(如Random的实例)不会直接存储它们的值。 相反,引用类型存储其值所在的地址。

按值传递和按引用传递的参数

将参数传递给方法时, 类型变量的值将复制到方法中。 每个变量都有自己的值副本,因此不会修改原始变量。

使用引用类型时,值的地址将传递到方法中。 传递给方法的变量引用该地址处的值,因此对该变量的操作会影响被另一个变量引用的值。

注释

请务必记住, string 这是一个引用类型,但它是不 可变的。 这意味着,一旦为其分配了值,便无法更改该值。 在 C# 中,当方法和运算符用于修改字符串时,返回的结果实际上是一个新的字符串对象。

在本练习中,你将了解有关将引用和值类型参数传递到方法的详细信息。

对按值传递进行测试

  1. 在 Visual Studio Code 编辑器中,删除前面练习中的任何现有代码。

  2. 在 Visual Studio Code 编辑器中输入以下代码:

    int a = 3;
    int b = 4;
    int c = 0;
    
    Multiply(a, b, c);
    Console.WriteLine($"global statement: {a} x {b} = {c}");
    
    void Multiply(int a, int b, int c) 
    {
        c = a * b;
        Console.WriteLine($"inside Multiply method: {a} x {b} = {c}");
    }
    

    变量 abc 传递给 Multiply 方法。 变量的值在方法执行过程中打印,并在方法完成后再次打印。

    整数是值类型,在传入方法时复制其值。 你认为 c 输出会是什么?

  3. 保存并运行代码以观察以下输出:

    inside Multiply method: 3 x 4 = 12
    global statement: 3 x 4 = 0
    

    请注意,c 的值仅在 Multiply 方法中更改。 在方法之外, c 保留其原始值。

对按引用传递进行测试

  1. 从 Visual Studio Code 编辑器中删除以前的代码。

  2. 在 Visual Studio Code 编辑器中输入以下代码:

    int[] array = {1, 2, 3, 4, 5};
    
    PrintArray(array);
    Clear(array);
    PrintArray(array);
    
    void PrintArray(int[] array) 
    {
        foreach (int a in array) 
        {
            Console.Write($"{a} ");
        }
        Console.WriteLine();
    }
    
    void Clear(int[] array) 
    {
        for (int i = 0; i < array.Length; i++) 
        {
            array[i] = 0;
        }
    }
    

    代码开始于 array 被初始化以包含一些整数值。 使用 PrintArray 方法显示这些值。 该方法 Clear 在数组上调用,然后再次打印该数组。

    数组是引用类型。 引用类型将其值的地址存储在内存中。 你认为输出会是什么?

  3. 保存并运行代码以观察以下输出:

    1 2 3 4 5 
    0 0 0 0 0
    

    请注意,数组在 Clear 方法范围之外仍会更改。 发生这种情况是因为 Clear 该方法更新了存储在每个地址中的值。

使用字符串进行测试

早些时候,你了解到字符串是 不可变 类型。 尽管字符串是引用类型,但与数组不同,在分配字符串后,无法更改其值。 如果您使用过如string.Replacestring.ToUpper等方法,您可能已经注意到这一点。 在此任务中,你将了解如何更正使用字符串时发现的常见错误。

  1. 在 Visual Studio Code 编辑器中,删除前面练习中的任何现有代码。

  2. 在 Visual Studio Code 编辑器中输入以下代码:

    string status = "Healthy";
    
    Console.WriteLine($"Start: {status}");
    SetHealth(status, false);
    Console.WriteLine($"End: {status}");
    
    void SetHealth(string status, bool isHealthy) 
    {
        status = (isHealthy ? "Healthy" : "Unhealthy");
        Console.WriteLine($"Middle: {status}");
    }
    
  3. 保存并运行代码以观察以下输出:

    Start: Healthy
    Middle: Unhealthy
    End: Healthy
    

    SetHealth如果方法未输出状态,则可能假定该方法未正确执行。 而是创建了一个值为“不正常”的新字符串,然后在方法的作用域内被丢弃。

    若要更正此问题,可以改为使用 SetHealth 全局状态变量。

  4. 按以下方式更新您的代码:

    string status = "Healthy";
    
    Console.WriteLine($"Start: {status}");
    SetHealth(false);
    Console.WriteLine($"End: {status}");
    
    void SetHealth(bool isHealthy) 
    {
        status = (isHealthy ? "Healthy" : "Unhealthy");
        Console.WriteLine($"Middle: {status}");
    }
    

    在此代码中,将使用新的字符串值覆盖全局 status 变量。

  5. 保存并运行代码以观察以下输出:

    Start: Healthy
    Middle: Unhealthy
    End: Unhealthy
    

    现在,已更新的字符串已捕获并正确存储。

回顾

下面介绍了到目前为止已了解的值类型和引用类型参数:

  • 变量可以归类为值类型和引用类型。
  • 值类型直接包含值,引用类型存储值的地址。
  • 使用值类型参数的方法创建其自己的值副本。
  • 对数组参数执行更改的方法会影响原始输入数组。
  • 字符串是不可变引用类型。
  • 对字符串参数执行更改的方法不会影响原始字符串。