C# dictionary value reference type - please explain why this happens

ε祈祈猫儿з 提交于 2020-02-24 12:43:06

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!