简体   繁体   English

方法将变量超出其范围

[英]methods change variable out of its scope

In this small piece of code, changing a 2 dimensional array inside a method leads to changes the variable in main method. 在这段小代码中,更改方法内部的二维数组会导致更改main方法中的变量。

What is the cause of this and how can I protect the variable in main method to remain unchanged? 这是什么原因,如何保护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]);
        }
    } 
} 

At the end this is the program output: 最后是程序输出:

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.

EDIT 1: A question come in my mind is : What are other common programming languages that doesn't have this sort of problem? 编辑1:我想到的一个问题是:还有其他没有这种问题的常见编程语言吗?

EDIT 2: For sack of comparison, I write this somehow identical code with string variable instead of array and the output is as expected and is just fine: 编辑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);
        }
    } 
}   

and the output of this code is : 该代码的输出为:

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.

Last EDIT : Thanks everybody for clarification, Hope this will become useful for others in future. 最后编辑:感谢大家的澄清,希望这对将来对其他人有用。

When you pass an array to a method (or any reference type), you're passing a reference to the memory where that array exists. 当将数组传递给方法(或任何引用类型)时,就是将引用传递给该数组所在的内存。 So making a change to the array will behave exactly the same as if you'd made that change in the method in which the array was originally instantiated. 因此,对数组进行更改的行为与在最初实例化数组的方法中进行的更改完全相同。

Unfortunately, there's no way to make an array read-only in C#. 不幸的是,无法在C#中将数组设为只读。 However, you could create a wrapper class as described here that provides a mechanism for accessing the values in the array, without providing a way to change the array. 但是,您可以按此处所述创建包装器类该包装器类提供了一种用于访问数组中的值的机制,而无需提供更改数组的方法。 Then you could pass that wrapper class into Function instead of passing the array itself. 然后,您可以将该包装器类传递给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];}
    }
}

Then: 然后:

    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]);
    }

Alternatively, you could make sure that you only give Function a copy of the original array. 另外,您可以确保仅向Function提供原始数组的副本 But that obviously has some performance implications. 但这显然会影响性能。

Response to Edits 回应编辑

The example you give to show how string variables work isn't really equivalent to the original. 您给出的用于说明字符串变量如何工作的示例实际上并不等同于原始变量。 In that second example, you are changing the value of the variable locally, but that value is just a memory address--you're not actually changing the string itself. 在第二个示例中,您正在本地更改变量的值,但是该值只是一个内存地址-您实际上并没有更改字符串本身。 You could do the same thing with an array like this: 您可以使用以下数组执行相同的操作:

    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]);
    }

By doing this, you are changing the value of the method_var variable , not the values in the array that it is pointing to. 这样,您更改的是method_var 变量的值,而不是它所指向的数组中的值。

Eric Lippert's comments below this post explain very clearly how C/C++ can give you read-only behavior on an array, but won't allow you to change the array's values locally without also changing them in the array that the calling method is referencing. 这篇文章下方的Eric Lippert的评论非常清楚地说明了C / C ++如何为您提供数组的只读行为,但不允许您在本地更改数组的值,而不必在调用方法所引用的数组中也更改它们。 He rightly points out that this is not a limitation of C#: it is a fundamental principle of how memory allocation works. 他正确地指出这不是C#的限制:这是内存分配工作原理的基本原理。 Values passed from one method to another can either be passed by copying all their contents or by just copying a reference to their contents. 从一种方法传递给另一种方法的值可以通过复制其所有内容或仅复制对其内容的引用来传递。 For small values, you can have a value type, and C# will pass their entire value. 对于较小的值,可以有一个值类型,C#将传递其整个值。 For larger values like arrays, it would be expensive and error-prone to copy their entire value, so the language will not attempt to do this automatically--you must do it explicitly. 对于较大的值(如数组),复制它们的整个值将很昂贵且容易出错,因此该语言不会尝试自动执行此操作-您必须明确地执行此操作。

I'd just simply use Copy() to get a new instance with the same content: 我只是简单地使用Copy()来获得具有相同内容的新实例:

string[,] newArray = new string[1, 1];
Array.Copy(string_variable , 0, newArray, 0, string_variable.Lenght);
Function(newArray);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM