tags:

views:

284

answers:

8
+5  Q: 

List<int> in c#

I am unable to understand the logic behind List<int> as it breaks some of the basic rules.

List<int> is supposed to be of value type and not reference type.

  1. List<int> has to be passed by ref keyword if its value has to be persisted between function calls. So this means it is displaying a value type behavior similar to int.
  2. But List<int> has to be initialized by a new operator. Also List<int> could be null as well. This implies a reference type behavior.

A nullable type is different as in it does not have to be initialized by new operator.

Am I seeing something wrong here?

EDITED-

I should have posted the code in the original question itself. But it follows here -

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            ListTest d = new ListTest();
            d.Test();
        }
    }

    class ListTest
    {
        public void  ModifyIt(List<int> l)
        {
            l = returnList();
        }

        public void Test()
        {
            List<int> listIsARefType = new List<int>();
            ModifyIt(listIsARefType);
            Console.WriteLine(listIsARefType.Count); // should have been 1 but is 0
            Console.ReadKey(true);
        }

        public List<int> returnList()
        {
            List<int> t = new List<int>();
            t.Add(1);
            return t;
        }
    }
}
+25  A: 

List is supposed to be of value type and not reference type.

Wrong! int is a value type. List<int> is a reference type.

Joel Coehoorn
If that were the case why pass by ref the List<int> between functions ?
Prashant
@Prash You would do that if you wanted to change the reference itself (ie: set it to null or a different list). But I wouldn't normally build code to make that necessary.
Joel Coehoorn
@Prashant: you don't need to. Whoever told you that you do, always, is a moron. What the `ref` keyword does, is allow you to set the parameter to a whole other `List<int>`. Which isn't even nearly as common as you seem to think it is.
cHao
Poor, old `ref`; so misunderstood. :)
Esteban Araya
+2  A: 

List<int> is indeed a reference type. But the items contained in the list are value types.

Nullable types however are implemented as structs (struct Nullable<T> where T : struct) and are therefore value types. The reason that you can simply write

int? i = 3;

without the new keyword is that the above syntax is translated by the compiler automatically into code that will do the following:

Nullable<Int32> i = new Nullable<Int32>(3);

To get a better understanding of the differences between value type and reference type semantics I would recommend you to read Jon Skeet's article on this topic which will guide you with lots of illustrative code sample:

Jon Skeet: Parameter passing in C#

0xA3
+1  A: 

A List is a generic reference type, you are using it with a value type int. But it is still a reference type.

Chris Diver
+2  A: 

Don't think of it as List<int> think of it the way that it was written List<t>.

List is a generic class. It is not a struct. It is a generic class that can work with value types and reference types.

Seattle Leonard
A: 

List<int> is a reference type. And it doesn't have to be passed as a reference.

The type of the objects in the list is kind-of a value type, except that value-type objects may end up boxed (converted into reference-type objects of a sort) anyway, so..scratch this paragraph once you understand that.

cHao
Note that `List<int>` does *not* require boxing. Only if you place a value type in a `List<object>` boxing will be involved.
0xA3
@0xA3: I wasn't sure about how well .net did that. I know Java's annoying about that, but its Integers are a half-hearted attempt to make primitives look like objects, whereas .net's "primitives" are actual objects.
cHao
+6  A: 

I think you have a faulty assumption in your first bullet. The generic List object is definitely a reference type (on the heap, not the stack). Not sure why you think you have to pass via ref. This prints "2" like it should:

namespace ConsoleApplication1 {
   class Program {
      static void Main(string[] args) {
         List<int> listIsARefType = new List<int>();
         ModifyIt(listIsARefType);
         ModifyIt(listIsARefType);
         Console.WriteLine(listIsARefType.Count); // 2!
         Console.ReadKey(true);
      }

      static void ModifyIt(List<int> l) {
         l.Add(0);
      }
   }
}
mattmc3
I think it's important to notice that any List<T> is a Reference Type due to the fact that it was created from a basetype that is a reference type.It should be noted that an item that is in the list is potentially not a reference type but it could become a reference type.
msarchet
+5  A: 

You need to understand the difference between pass by reference, pass by value, and pass reference by value.

In the code sample you posted, you're passing the reference to the List<int> object by value. This means that you can mutate the object pointed to by the reference, and the calling code will see these changes. However, the reference itself is passed by value, so if you change the reference to point to a different object altogether, the calling code will not see the changes.

When you use the ref keyword, you're passing the reference itself by reference. This means that not only can you change the object that the reference is pointing to, but you can also change the reference itself.

Consider this example:

class Program
{
    static void Main()
    {
        int foo = 0;
        DoSomething1(foo);
        Console.WriteLine(foo); // Outputs 0.

        DoSomething1(ref foo);
        Console.WriteLine(foo); // Outputs 1.

        var bar = new List<int>();
        DoSomething2(bar);
        Console.WriteLine(bar.Count); // Outputs 1.

        DoSomething2(ref bar);
        Console.WriteLine(bar.Count); // Outputs 0.
    }

    // Pass by value.
    static void DoSomething1(int number)
    {
        // Can't modify the number!
        number++;
    }

    // Pass by value.
    static void DoSomething1(ref int number)
    {
        // Can modify the number!
        number++;
    }

    // Pass reference by value.
    static void DoSomething2(List<int> list)
    {
        // Can't change the reference, but can mutate the object.
        list.Add(25);
    }

    // Pass reference by reference.
    static void DoSomething2(ref List<int> list)
    {
        // Can change the reference (and mutate the object).
        list = new List<int>();
    }
}
Will Vousden
A: 

In addition to the mistaken assumptions dealt with in other answers, you say:

List<int> has to be initialized by a new operator... This implies a reference type behavior.

No, in C# the new operator is just the syntax for calling the constructor of a type. It's used for both reference and user-defined value types (structs).

Daniel Earwicker