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

Scr*_*cre 18 c# pass-by-reference pass-by-value

从C/C++迈出C#世界的第一步,细节上有点模糊.据我所知,类默认通过引用传递,但是例如.列出<string>,如:

void DoStuff(List<string> strs)
{
    //do stuff with the list of strings
}
Run Code Online (Sandbox Code Playgroud)

和其他地方

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

SL在这种情况下按引用传递或者是副本上进行,这样我需要重新定义,如工人功能

void DoStuff(ref List<string> strs)
实际上是对sl本身采取行动而不是副本?

Adr*_*ala 15

它通过引用传递. List<T>是一个类,所有类实例都通过引用传递.


Ale*_*ger 9

行为总是一样的:通过复制传递.如果该参数是一个对象,则引用的对象被复制,所以实际上你是同一个对象/表/无论在工作.


Ada*_*rth 5

底层的事情始终是:值类型是按值传递的,而引用类型是“按引用传递的”(引用是因为引用的值实际上是按值传递的,但大多数人为了简洁而忽略了这一点)。

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

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

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

上面的解释无耻地摘自Jon Skeet 关于该主题的文章

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

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

void DoSomething(List<string> strs) 
{ 
    strs.Add("Hello");
}
Run Code Online (Sandbox Code Playgroud)


Zed*_*Bee 5

除了其他答案之外,了解 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
Run Code Online (Sandbox Code Playgroud)