【问题标题】:Does C# pass a List<T> to a method by reference or as a copy? [duplicate]C# 是通过引用还是作为副本将 List<T> 传递给方法? [复制]
【发布时间】:2014-06-22 20:04:58
【问题描述】:

从 C/C++ 开始我在 C# 世界中的第一步,所以细节有点模糊。据我了解,默认情况下,类是通过引用传递的,但是例如。列表 像在:

void DoStuff(List<string> strs)
{
    //do stuff with the list of strings
}

其他地方

List<string> sl = new List<string>();
//next fill list in a loop etc. and then do stuff with it:
DoStuff(sl);

在这种情况下是 sl 通过引用传递还是复制,所以我需要重新定义工作函数,如

void DoStuff(ref List strs)
实际作用于 sl 本身而不是副本?

【问题讨论】:

  • class 通过引用传递。 struct 按值传递。
  • this 文章应该可以帮助您更好地了解这个领域。
  • 似乎 C# 和 C++ 中的“按引用传递”来自不同的字典。无论如何,谢谢大家的回答——模糊的细节现在不那么模糊了。
  • @Scre 有些人在 C# 中以不同的含义混淆使用“按引用传递”,因为术语“引用”和“引用类型”(实际上是指“引用”的不同含义)。但是在 C# 中正确使用“按引用传递”,ref 参数,几乎与一般 C++ 和 CS 中称为按引用传递的东西相同。

标签: c# pass-by-reference pass-by-value


【解决方案1】:

如果要修改原始列表,方法中的ref 关键字是多余的:List&lt;T&gt; 是引用类型(C# 中的class),因此将通过引用传递给方法;因此该方法将操纵原始列表。

当传递Value Type 时,它将创建值本身的副本。 当传递Reference Type 时,它将创建引用的副本。

详细了解 C# 中的 Value and Reference Types

【讨论】:

  • ref 不是多余的。有了它,strs = ...; 会影响调用者。没有它,它就不会。
  • 另外,将引用类型对象发送到没有ref 关键字的方法会导致创建引用的新副本。 “冗余”意味着它是相同的。事实并非如此。
【解决方案2】:

它是通过引用传递的。 List&lt;T&gt;是一个类,所有的类实例都是通过引用传递的。

【讨论】:

    【解决方案3】:

    行为总是相同的:通过复制传递。如果参数是对象,则复制对象的引用,因此实际上您正在处理相同的对象/列表/任何内容。

    【讨论】:

      【解决方案4】:

      底层的东西总是:值类型是按值传递的,而引用类型是“按引用传递的”(引用是因为引用的值实际上是按值传递的,但为了简洁起见,大多数人忽略了这一点) .

      协调ref 关键字与引用的最简单方法是:引用类型的引用按值传递。在标准情况下,这具有简单地将对列表(而不是整个列表)的引用传递给方法的效果。

      ref 关键字,当用于引用类型时,在语义上传递对引用的引用(我真的很难不说“指向指针的指针”)。

      如果您的方法将ref 参数重新分配给一个新对象,调用者也会看到这个新分配。而如果没有 ref 关键字,该方法将简单地重新分配他们自己的引用值的本地副本,并且调用者仍然会引用其原始对象。

      以上解释无耻取自Jon Skeet's article on the topic

      这种差异对于理解参数绝对至关重要 传入 C#,这就是为什么我认为这样说非常令人困惑的原因 默认情况下,对象是通过引用而不是正确的 声明对象引用默认按值传递。

      ref 关键字仅在您打算重新分配参数并使其对调用者可见时才需要。在大多数情况下,您会发现它是不需要的。您的 DoStuff 可以重写以将其删除,并且仍然可以成功地按值传递对列表的引用:

      void DoSomething(List<string> strs) 
      { 
          strs.Add("Hello");
      }
      

      【讨论】:

      • 这是我对它如何工作的第一个“预感”,但不太确定。
      • @scre 我强烈推荐阅读 Jon Skeet 的那篇文章,并拿起他的 C# in Depth 书,他有很好的技巧来组织抽象或复杂主题的解释,以便于阅读。跨度>
      【解决方案5】:

      它的参考。不必包含“ref”。

      最好的问候。

      【讨论】:

      • ref 不是多余的。有了它,strs = ...; 会影响调用者。没有它,它就不会。
      • (参考值不需要)
      【解决方案6】:

      列表通过引用传递。这实际上意味着方法内部的 strs 变量引用了与方法外部的 sl 变量相同的列表。 如果你会使用 ref,你实际上可以在方法中重新分配 sl 变量。

      strs = new List<string>()
      

      将使 sl 指向新列表。

      由于您来自 C/C++: ref 可以被视为“安全指针”。类似于使用 &strs

      【讨论】:

        【解决方案7】:

        除了其他答案之外,了解 ref 的行为非常重要

        这里有一些示例代码用于演示目的

        static void Main(string[] args)
            {
        
                List<string> lstStr = new List<string>();
        
                lstStr.Add("First");
                lstStr.Add("Second");
        
                Alter(lstStr);
        
                //Alter(ref lstStr);
        
                Console.WriteLine("---From Main---");
                foreach (string s in lstStr)
                {
                    Console.WriteLine(s);
                }
        
                Alter2(ref lstStr);
        
                Console.WriteLine("---From Main after passed by ref---");
                foreach (string s in lstStr)
                {
                    Console.WriteLine(s);
                }
        
                Console.ReadKey();
            }
        
            static void Alter(List<string> lstStr2)
            {
                lstStr2.Add("Third");
        
                Console.WriteLine("----From Alter----");
                foreach (string s in lstStr2)
                {
                    Console.WriteLine(s);
                }
        
                lstStr2 = new List<string>();
                lstStr2.Add("Something new");
        
                Console.WriteLine("----From Alter - after the local var is assigned somthing else----");
        
                foreach (string s in lstStr2)
                {
                    Console.WriteLine(s);
                }
        
            }
        
            static void Alter2(ref List<string> lstStr2)
            {
                lstStr2 = new List<string>();
                lstStr2.Add("Something new from alter 2");
        
                Console.WriteLine("----From Alter2 - after the local var is assigned new list----");
        
                foreach (string s in lstStr2)
                {
                    Console.WriteLine(s);
                }
        
            }
        
        
        //----From Alter----
        //First
        //Second
        //Third
        //----From Alter - after the local var is assigned somthing else----
        // Something new
        // ---From Main---
        // First
        // Second
        // Third
        // ----From Alter2 - after the local var is assigned new list----
        // Something new from alter 2
        // ---From Main after passed by ref---
        // Something new from alter 2
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-02-03
          • 2016-06-11
          • 2020-04-01
          • 2018-06-29
          • 1970-01-01
          • 2012-07-08
          • 2011-05-06
          • 1970-01-01
          相关资源
          最近更新 更多