为什么在方法中反转字符串数组不会持续存在

本文关键字:存在 数组 字符串 方法 为什么 | 更新日期: 2023-09-27 18:09:13

所以我有一个简单的字符串数组。将其传递给一个函数,该函数反转其输入,然后显示数组的内容。我期望数组的内容反转,因为数组是通过引用传递的,但字符串数组没有改变。

string[] words = { "Metal", "Gear", "is", "Awesome!" };
mutateArray(ref words);
foreach (string word in words)
     Console.Write(word + " "); 
这是我的mutateArray函数:
 public static void mutateArray(ref string[] arr)
 {
      arr = arr.Reverse().ToArray();          
 }

我知道,一旦我声明参数必须与关键字ref一起传递,mutateArray方法对数组的更改将持续存在。

    默认情况下不是所有的数组都通过引用传入吗?
  1. 当涉及关键字ref时,为什么更改持续存在?
  2. 通过值传递引用类型(classes, interfaces, array, delegates)与通过引用(使用关键字ref)传递它们之间的区别是什么?

为什么在方法中反转字符串数组不会持续存在

这不能像您期望的那样工作的原因是Reverse()实际上并没有反转数组的内容,而是用原始数组的反转内容创建一个新的列表。这就是为什么当你通过引用传递数组时它会工作:然后,你实际上是用mutateArray中创建的新数组替换了调用方法中的整个原始数组。

如果您有一个方法可以在适当的位置进行反转,您可以传入原始数组(不使用ref),并且在方法调用之后,数组将以相反的顺序排列。

  1. c#默认情况下所有参数都是按值传递的。对于像数组这样的引用类型,这意味着引用是按值传递的。

  2. ref使变量通过引用传递给函数。这实际上意味着mutateArray中的arr参数是调用方中words的别名。这就是为什么在mutateArray退出后,arr的赋值会导致words的变化。

  3. 按值将引用类型传递给函数意味着生成了引用的副本。如果没有ref修饰符,mutateArray中的arr是一个不同的变量,包含对调用方中的words相同对象的引用。在这种情况下,分配给arr对调用方中的words没有影响。注意,您可以通过共享引用改变数组,但是arrwords是单独的存储位置。

  1. 你混淆了引用类型和引用传递。ref关键字可以应用于Value和Reference类型。即使是引用类型,默认情况下也不会通过引用传递。

  2. 根据MSDN的文档,他们应该。这就是使用ref关键字和引用类型的全部目的。

  3. 不同之处在于,当您通过引用类型传递时,您可以更改原始变量的引用,而不仅仅是方法中的实例。

words是对数组的引用。只需考虑它包含该数组的内存地址。

当你把它作为参数给MutateArray时(没有ref关键字),它的VALUE将被复制到arr中。所以arr和words是不同的变量,但是它们包含相同的值(=内存地址)。这意味着它们指向同一个对象(字符串数组)。

你可以改变对象的内容,但是words(和arr)仍然会引用它。

如果你将arr赋值给一个不同的对象,那么它的值就会改变,所以它将指向一个不同于words的对象。

但是,如果使用ref关键字,则arr和words是同一个变量。这意味着如果你改变了arr的值(=将其赋值给一个新对象),你也改变了words的值,因此words将指向相同的新对象。

也许所有这些在技术上不是100%正确,但这是我喜欢思考的方式,以便理解它是如何工作的。

ref关键字是下面的Swap方法在c#中工作的原因;如果没有它,它只会改变Swap方法的内部变量(基本上什么都不做)

public void Swap<T>(ref T a, ref T b) {
    T temp = a;
    a = b;
    b = temp;
}

你的问题是完全可以理解的,那一刻我也很困惑。
下面是解释

简短的解释
  • 当你把一个引用类型传递给一个方法并改变它的属性时,方法外的对象可以看到属性的改变,因为对象本身保持不变。
  • 当你将引用类型传递给一个方法并对实例本身进行更改时,方法外部的对象无法看到更改,因为在方法内部你基本上开始指向另一个对象。因此,对象在方法内部发生了变化,并作为一个陌生人留在那里。
长解释

  • 假设你有一个从数据库中获取值的引用类型实例。

        using (var context = new MyAdventureWorksEntities2())
        {
            Product p = context.Products.Where(item => item.ProductID == 1000).First();
            Console.WriteLine(p.Name); // p.Name = "INITIAL NAME"
            UpdateName(p);
            Console.WriteLine(p.Name);
        }
    

这是你的UpdateName方法:

    public static void UpdateName(Product p)
    {
        p.Name = "UPDATED NAME";
    }

这段代码发出如下结果:INITIAL NAME
UPDATED NAME

  • 然而,如果您将方法更改为以下内容:

    public static void UpdateName(Product p)
    {
        using (var context = new MyAdventureWorksEntities2())
        {
            p = context.Products.Where(item => item.ProductID == 1003).First();
            // p.Name = "ANOTHER PRODUCT NAME"
        }
    }
    

你的结果将是:
INITIAL NAME
INITIAL NAME

请注意,我根本没有触及ref关键字。
也许在这些例子之后,简短的描述会更容易理解。