C# 是通过引用还是作为副本将 List<T> 传递给方法? [复制]

Posted

技术标签:

【中文标题】C# 是通过引用还是作为副本将 List<T> 传递给方法? [复制]【英文标题】:Does C# pass a List<T> to a method by reference or as a copy? [duplicate] 【发布时间】: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 中称为按引用传递的东西相同。 【参考方案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

【讨论】:

以上是关于C# 是通过引用还是作为副本将 List<T> 传递给方法? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥 C# 数组对 Enumeration 使用引用类型,而 List<T> 使用可变结构?

如何告诉 lambda 函数捕获副本而不是 C# 中的引用?

List<T> C# 查找特定行

Java ArrayList 副本

将 JSON 数组反序列化为 List<T> C# 时出错

WCF 服务参考作为数组与 List<T>