问题
I don't understand the results of the following linqpad query in C#. The comments should explain where I am confused.
void Main()
{
Dictionary<string, testClass> test = new Dictionary<string, testClass>();
string key = "key";
testClass val = null;
test.Add(key, val);
val = new testClass();
test[key].Dump(); //returns null. WHAT? I just set it!!!
test[key] = val;
val.Text = "something";
// returns val object, with Text set to "Something".
// If the above didn't work, why does this work?
test[key].Dump();
val.Text = "Nothing";
// return val object, with Text set to "Nothing".
// This, I expect, but, again, why didn't the first example work?
test[key].Dump();
val = null;
// returns val object, with Text set to "Nothing"...WHAT??
// Now my head is going to explode...
test[key].Dump();
}
// Define other methods and classes here
public class testClass
{
public override string ToString()
{
return Text;
}
public string Text { get; set;}
}
回答1:
The main reason is that the variable (val
) is not the object. It merely contains a reference to an object (or null).
testClass val = null
declares a variable of type testClass
and sets it to null. It doesn't point to any object.
test.Add(key, val)
adds an entry in the dictionary that points to null (note: it doesn't point to val
nor does it point to any object).
val = new testClass();
creates a new instance of testClass
and val
now points to that new object. test[key]
is still null and doesn't point to any object.
test[key] = val;
val.Text = "something";
test[key].Dump();
This code points test[key]
to the same object that val
points to. Again note is it not pointing to val
. When you change the object with val.Text = "something"
you can see the change using test[key].Dump()
because they both point to the same object.
val.Text = "Nothing";
test[key].Dump();
When you set val.Text
to the string "Nothing" you can see the change through test[key]
for the same reason as above, they both point to the same object.
val = null;
test[key].Dump();
This code sets val
to point at null. test[key]
still points at the object. Now val
and test[key]
point to different things.
回答2:
test.Add(key, val);
val = new testClass();
test[key].Dump(); //returns null. WHAT? I just set it!!!
You are re-instantiating val
. It is no longer pointing to the same object that you added to test
. When you new up a reference object, it points to a brand new object.
If, say, you had an int
property on testClass
and did:
var c = new testClass{ MyProperty = 1}
test.Add(key, c);
c.MyProperty = 2;
test[key].MyProperty.Dump();
you would see 2
outputted, because you didn't change the object that c
is pointing to, but altered a property on the existing object.
回答3:
testClass val = null;
test.Add(key, val);
val
refers to null
. You just added a null
to your dictionary, under the key key
.
val = new testClass();
You just assigned a new instance of testClass
to your variable named val
. The dictionary has no way of knowing that you did that. It doesn't know about val
; all you gave it was the value that val
held at that time. Think of it as keeping a copy of what val
held then.
If you put write "4" on a piece of paper and take a photo of it, then you erase it and write "5", the photo still shows "4".
test[key].Dump(); //returns null. WHAT? I just set it!!!
You set test[key]
to null
, two lines ago. test[key]
isn't a reference to "whatever val
refers to now"; it's whatever value you passed to the Dictionary.Add()
method. That value was null
. The dictionary keeps exactly what you gave it, until you give it something else.
test[key] = val;
OK, now you gave it something else. You gave it the new value of val
.
val.Text = "something";
// returns val object, with Text set to "Something".
// If the above didn't work, why does this work?
test[key].Dump();
...and that's why this works.
val.Text = "Nothing";
// return val object, with Text set to "Nothing".
// This, I expect, but, again, why didn't the first example work?
test[key].Dump();
The dictionary still has that new value of val
-- and val
is still referring to that same object (think of it as hanging in space, off the shoulder of Orion). And now you've altered one of the properties of that one object that they both happen to be referring to.
val = null;
Whoops, they're not both referring to the same object any more. The dictionary is still referring to that object, but val
is now referring to nothing at all.
// returns val object, with Text set to "Nothing"...WHAT??
// Now my head is going to explode...
test[key].Dump();
And you still haven't given the dictionary anything new, so it still has the last thing you gave it.
Don't feel too bad about this. I taught C# last year to my brother, an MIT PhD in electrical engineering. He got badly snarled in this same issue -- and he's the guy who patiently taught how pointers work in C, back in 1995.
回答4:
Let me try to explain it in code comments:
void Main()
{
Dictionary<string, testClass> test = new Dictionary<string, testClass>();
string key = "key";
testClass val = null;
//val now holds a null reference
test.Add(key, val);
//You add a null reference to the dictionary
val = new testClass();
//Now val holds a new reference for testClass, while the dictionary still has null
test[key].Dump(); //returns null. WHAT? I just set it!!!
//Now it returns the null reference which you just added to the dictionary
test[key] = val;
//Now the dictionary holds the same reference as val
val.Text = "something";
//You set the Text of the val, and the dictionary-held value, because they're the same
// returns val object, with Text set to "Something".
// If the above didn't work, why does this work?
test[key].Dump();
// I think now you know why..
//...
}
回答5:
Look at the following code:
List<int> lst = new List<int>();
SomeFunc(lst);
Here is SomeFunc():
void SomeFunc(List<int> l)
{
l = null;
}
Calling SomeFunc()
will not change the value of variable lst
in any way. This is because l
gets a copy of lst
. Thus both lst
and l
point to the same memory object, but both are distinct themselves and changing the contents of one variable has no effect on the value of other.
In a way you can think of reference types as classical pointers, although they are different in many important ways. Having two pointer pointing to the same object doesn't mean those two pointers have any effect on each other's value, and setting one of them to a different value (or null) doesn't change the value of other.
回答6:
You need to be aware of the difference of value and reference types.
In this case, you have a reference variable with the name val, it's address to the object in the memory is null. When you are calling "test.Add(key, val);" and val equals null, then there is object behind this reference variable. In the next line, you initialize the reference variable, now there is a object behind it.
So these results make absolutely sense.
Look here for more information: Value and Reference Types
回答7:
C# uses reference types for objects.
1)In the first example you are adding a pair of key and a value which is a reference type. The reference is pointing to null so you actually add (key,null).
2) Now you have created an instance of an object. The val is referencing that object and when you assign it test[key] = val you are actually assigning the object that the val is referencing.
3)The object referenced in the dictionary and the val variable is the same . So you are accessing the same object.
4)You have set the val to null so it doesn't reference anything but in the dictionary it is still referencing to that old object you have created.
来源:https://stackoverflow.com/questions/36225847/c-sharp-dictionary-value-reference-type-please-explain-why-this-happens