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.
List<int>
has to be passed byref
keyword if its value has to be persisted between function calls. So this means it is displaying a value type behavior similar to int.- But
List<int>
has to be initialized by a new operator. AlsoList<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开发者_高级运维;
}
}
}
List is supposed to be of value type and not reference type.
Wrong! int
is a value type. List<int>
is a reference type.
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);
}
}
}
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>();
}
}
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.
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#
A List
is a generic reference type, you are using it with a value type int. But it is still a reference type.
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 (struct
s).
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.
In ModifyIt() method, when you are writing 'l = returnList()'; 'l' is now pointing to a different location in the memory than that of listIsARefType in your Test() method. Basically by writing 'l' '=' something, you have broken the link between 'l' and 'listIsARefType'. In order to keep the link (that is making sure that both of the objects, 'l' and 'listIsARefType' are pointing to the same location in the memory), you either need to only work on the 'l' object (for example by calling a function on the object), or use the ref keyword in the parameter of the ModifyIt() method.
精彩评论