Does C# pass a List<T> to a method by reference or as a copy?

52,278

Solution 1

It's passed by reference. List<T> is a class, and all class instances are passed by reference.

Solution 2

The behaviour is always the same: Passing by copying. In case the parameter is an object, the reference to the object is copied, so in fact you are working on the same object/list/whatever.

Solution 3

In addition to other answers it is very important to understand the behavior of ref

Here is some sample code for demonstration purpose

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

Solution 4

The underlying thing always is: value types are passed by value, and reference types are "passed by reference" (quoted because the value of the reference is actually passed by value, but most people ignore that for the sake of brevity).

The easiest way to reconcile the ref keyword against references is: reference types have their reference passed by value. This has the effect, in the standard case, of simply passing the reference to the list (and not the entire list) to the method.

The ref keyword, when used on a reference type, semantically passes a reference to the reference (I really struggle not to say "pointer to a pointer").

If your method were to re-assign the ref argument to a new object, the caller would also see this new assignment. Whereas without the ref keyword, the method would simply be re-assign their own local copy of the reference value and the caller would still have a reference to their original object.

The above explanation is shamelessly taken from Jon Skeet's article on the topic:

This difference is absolutely crucial to understanding parameter passing in C#, and is why I believe it is highly confusing to say that objects are passed by reference by default instead of the correct statement that object references are passed by value by default.

The ref keyword is only needed if you intend to re-assign the argument and have that visible to the caller. In most cases you will find that it isn't needed. Your DoStuff can be re-written to remove it and still pass a reference to the list by value successfully:

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

Solution 5

The ref keyword in your method is redundant if you want to modify the original list: List<T> is a reference type (class in C#) and so will be passed to the method by reference; therefore the method will manipulate the original list.

When passing a Value Type, it will create a copy of the value itself. When passing a Reference Type, it will create a copy of the reference.

Read more about Value and Reference Types in C#.

Share:
52,278

Related videos on Youtube

Scre
Author by

Scre

I code (mainly in C++, C#, and Kotlin, but do tinker with Ruby, Lua, and various other lingos). I play computer games, of course... lots of them. That's about all there is to say at the moment.

Updated on April 30, 2020

Comments

  • Scre
    Scre about 4 years

    Taking my first steps in C# world from C/C++, so a bit hazy in details. Classes, as far as I understood, are passed by reference by default, but what about eg. List<string> like in:

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

    and elsewhere

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

    Is sl in this case passed by reference or is a copy made so that I'd need to redefine the worker function like

    void DoStuff(ref List<string> strs)
    to actually act on sl itself and not a copy?

    • Dialecticus
      Dialecticus almost 10 years
      class is passed by reference. struct is passed by value.
    • Kurubaran
      Kurubaran almost 10 years
      this article should help you understand this area better.
    • Scre
      Scre almost 10 years
      Seems "pass by reference" in C# and C++ are from different dictionaries. Anyhow, thank you all for the answers - hazy details now less hazy.
    • Admin
      Admin almost 10 years
      @Scre Some people confusingly use "pass by reference" in a different sense in C#, because of the terms "reference" and "reference type" (which indeed refer to a different meaning of "reference"). But the correct use of "pass by reference" in C#, ref parameters, is almost identical to the thing called pass by reference in C++ and CS in general.
  • Admin
    Admin almost 10 years
    The ref is not redundant. With it, strs = ...; affects the caller. Without it, it doesn't.
  • Admin
    Admin almost 10 years
    The ref is not redundant. With it, strs = ...; affects the caller. Without it, it doesn't.
  • Tarec
    Tarec almost 10 years
    Also, sending a reference type object to a method without ref keyword results in creating a new copy of the reference. "Redundant" would mean it's the same. And it is not.
  • Scre
    Scre almost 10 years
    This was my first "hunch" about how it would work, but wasn't quite sure.
  • Adam Houldsworth
    Adam Houldsworth almost 10 years
    @scre I highly recommend reading that Jon Skeet article and also picking up the C# in Depth book by him, he has a good skill for structuring an explanation of abstract or complex topics for easy consumption.
  • fabian
    fabian almost 10 years
    (Not necesary for reference values)