方法将变量更改为其范围

Woe*_*itg 0 c# arrays multidimensional-array

在这一小段代码中,更改方法内的二维数组会导致更改main方法中的变量.

造成这种情况的原因是什么?如何保护main方法中的变量保持不变?

using System;

namespace example
{
    class Program
    {
        static void Main(string[] args)
        {
            string[,] string_variable = new string[1, 1];
            string_variable[0, 0] = "unchanged";
            Console.Write("Before calling the method string variable is {0}.\n", string_variable[0,0]);
            Function(string_variable);
            Console.Write("Why after calling the method string variable is {0}? I want it remain unchanged.\n", string_variable[0, 0]);
            Console.ReadKey();
        } 
        private static void Function(string [,] method_var)
        {
            method_var[0, 0] ="changed";
            Console.Write("Inside the method string variable is {0}.\n", method_var[0, 0]);
        }
    } 
} 
Run Code Online (Sandbox Code Playgroud)

最后这是程序输出:

Before calling the method string variable is unchanged.
Inside the method string variable is changed.
Why after calling the method string variable is changed? I want it remain unchanged.
Run Code Online (Sandbox Code Playgroud)

编辑1:我想到的一个问题是:什么是其他常见的编程语言没有这种问题?

编辑2:对于大量的比较,我用字符串变量而不是数组写这个相同的代码,输出是预期的,并且很好:

using System;
namespace example
{
    class Program
    { 
    static void Main(string[] args)
        {
            string string_variable;
            string_variable= "unchanged";
            Console.Write("Before calling the method string variable is {0}.\n", string_variable);
            Function(string_variable);
            Console.Write("after calling the method string variable is {0} as expected.\n", string_variable);
            Console.ReadKey();
        } 
        private static void Function(string method_var)
        {
            method_var ="changed";
            Console.Write("Inside the method string variable is {0}.\n", method_var);
        }
    } 
}   
Run Code Online (Sandbox Code Playgroud)

并且此代码的​​输出是:

Before calling the method string variable is unchanged.
Inside the method string variable is changed.
after calling the method string variable is unchanged as expected.
Run Code Online (Sandbox Code Playgroud)

最后编辑:感谢大家的澄清,希望这将在未来对其他人有用.

Str*_*ior 5

将数组传递给方法(或任何引用类型)时,您将向该数组所在的内存传递引用.因此,对数组进行更改的行为与在最初实例化数组的方法中进行更改的行为完全相同.

不幸的是,没有办法在C#中将数组设为只读.但是,您可以创建一个如此处所述的包装类,它提供了一种访问数组中值的机制,而无需提供更改数组的方法.然后你可以将该包装类Function传递给而不是传递数组本身.

public class ReadOnlyTwoDimensionalArray<T>
{
    private T[,] _arr;
    public ReadOnlyTwoDimensionalArray(T[,] arr)
    {
        _arr = arr;
    }
    public T this[int index1, int index2]
    {
        get {return _arr[index1, index2];}
    }
}
Run Code Online (Sandbox Code Playgroud)

然后:

    static void Main(string[] args)
    {
        string[,] string_variable = new string[1, 1];
        string_variable[0, 0] = "unchanged";
        Console.Write("Before calling the method string variable is {0}.\n", string_variable[0,0]);
        Function(new ReadOnlyTwoDimensionalArray<string>(string_variable));
        Console.Write("Can't touch this: {0}.\n", string_variable[0, 0]);
        Console.ReadKey();
    } 
    private static void Function(ReadOnlyTwoDimensionalArray<string> method_var)
    {
        // the compiler will complain if you try to do this:
        //method_var[0, 0] ="changed"; 
        // but this works just fine
        Console.Write("Inside the method string variable is {0}.\n", method_var[0, 0]);
    }
Run Code Online (Sandbox Code Playgroud)

或者,您可以确保只提供原始数组Function副本.但这显然有一些性能影响.

对编辑的回应

您给出的示例表明字符串变量如何工作并不等同于原始变量.在第二个示例中,您将在本地更改变量的值,但该值只是一个内存地址 - 您实际上并未更改字符串本身.你可以用这样的数组做同样的事情:

    private static void Function(string [,] method_var)
    {
        method_var = new string[1, 1] {{"changed"}};
        Console.Write("Inside the method string variable is {0}.\n", method_var[0, 0]);
    }
Run Code Online (Sandbox Code Playgroud)

通过这样做,您将更改method_var 变量的值,而不是它指向的数组中的值.

Eric Lippert在本文下面的评论非常清楚地解释了C/C++如何在数组上为您提供只读行为,但是不允许您在本地更改数组的值而不在调用方法引用的数组中更改它们.他正确地指出这不是C#的限制:它是内存分配如何工作的基本原则.从一个方法传递到另一个方法的值可以通过复制其所有内容或仅复制对其内容的引用来传递.对于较小的值,您可以使用值类型,C#将传递其整个值.对于像数组这样的较大值,复制它们的整个值会很昂贵且容易出错,因此语言不会尝试自动执行此操作 - 您必须明确地执行此操作.

  • @MyUserName:但数组是*如何在C#中传递变量*.你说这是一件坏事,但一切都在工作,因为它是专门为工作而设计的.如果要创建一个无法被被调用者更改的集合,则传递一个**immutable**集合.如果要传递可以更改的集合,但是与调用者中的变量集不同*变量集,则传递**副本**.如果您希望调用者能够更改变量但被调用者只能读取它们,则传递**ReadOnlyCollection**. (2认同)