C#字典值引用类型 - 请解释为什么会发生这种情况

时间:2016-03-25 18:35:20

标签: c# dictionary pass-by-reference

我不理解C#中以下linqpad查询的结果。评论应该解释我感到困惑的地方。

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;}     
}

7 个答案:

答案 0 :(得分:6)

主要原因是变量(val)不是对象。它只包含对象的引用(或null)。

testClass val = null声明类型为testClass的变量并将其设置为null。它没有指向任何对象。

test.Add(key, val)在字典中添加一个指向null的条目(注意:它指向val,也不指向任何对象。)

val = new testClass();创建了testClass实例,val现在指向该新对象。 test[key] 仍然为空,并且不指向任何对象。

test[key] = val;
val.Text = "something";
test[key].Dump(); 

此代码将test[key]指向val指向的同一对象。再次注意是否指向val 。使用val.Text = "something"更改对象时,您可以使用test[key].Dump()查看更改,因为它们都指向同一个对象。

val.Text = "Nothing";
test[key].Dump(); 

当您将val.Text设置为字符串“Nothing”时,由于与上述相同的原因,您可以通过test[key]看到更改,它们都指向同一个对象

val = null;
test[key].Dump(); 

此代码将val设置为null。 test[key] 仍然指向该对象。现在valtest[key]指向不同的事物。

答案 1 :(得分:5)

test.Add(key, val);
val = new testClass();
test[key].Dump(); //returns null.   WHAT? I just set it!!!

您正在重新实例化val。它不再指向您添加到test的同一对象。当您新建一个参考对象时,它指向一个全新的对象。

例如,如果您在int上有testClass属性并且确实:

var c = new testClass{ MyProperty = 1}
test.Add(key, c);

c.MyProperty = 2;
test[key].MyProperty.Dump();

看到输出的2,因为你没有更改c指向的对象,但更改了现有对象的属性。

答案 2 :(得分:3)

testClass val = null;

test.Add(key, val);

val是指null。您刚刚在密钥null下的字典中添加了key

val = new testClass();

您刚刚为名为testClass的变量分配了val的新实例。字典无法知道你那样做了。它不知道val;你给它的全部是val当时持有的价值。可以把它想象成保留val所持有的内容的副本。

如果你在一张纸上写下“4”并拍下它的照片,那么你将其删除并写上“5”,照片仍显示为“4”。

test[key].Dump(); //returns null.   WHAT? I just set it!!!

您在两行之前将test[key]设置为nulltest[key]不是“现在val所指的任何内容”;它是您传递给Dictionary.Add()方法的任何值。该值为null。字典完全保留了你给它的字样,直到你给它别的东西。

test[key] = val;

好的,现在你给了别的东西。你给了它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的新值 - 而val仍然指的是同一个对象(想象它悬挂在太空中,离开猎户座的肩膀)。现在你已经改变了它们碰巧引用的那个对象的一个​​属性。

val = null;

哎呀,他们不再是指同一个对象了。字典仍然指的是该对象,但val现在指的是什么都没有。

//  returns val object, with Text set to "Nothing"...WHAT?? 
//  Now my head is going to explode...
test[key].Dump(); 

你仍然没有给字典任何新东西,所以它仍然是你给它的最后一件事。

不要觉得这太糟糕了。去年,我教给我的兄弟C#,他是麻省理工学院电气工程博士。他在同样的问题上受到严重的骚扰 - 他是那个在1995年耐心地教导指针如何在C中工作的人。

答案 3 :(得分:2)

让我试着在代码评论中解释一下:

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..

    //...
}

答案 4 :(得分:0)

请查看以下代码:

List<int> lst = new List<int>();
SomeFunc(lst);

这是SomeFunc():

void SomeFunc(List<int> l)
{
  l = null;
}

调用SomeFunc()不会以任何方式更改变量lst的值。这是因为l获得了lst的副本。因此lstl都指向同一个内存对象,但两者本身都是不同的,改变一个变量的内容对其他变量的值没有影响。

在某种程度上,您可以将引用类型视为经典指针,尽管它们在许多重要方面都有所不同。将两个指针指向同一个对象并不意味着这两个指针对彼此的值有任何影响,并将其中一个设置为不同的值(或为null)并不会更改该值其他的。

答案 5 :(得分:0)

您需要了解值和引用类型的区别。

在这种情况下,你有一个名为val的引用变量,它对内存中对象的地址为null。 当你调用“test.Add(key,val);”时和val等于null,然后这个引用变量后面有对象。 在下一行中,初始化引用变量,现在后面有一个对象。

所以这些结果绝对有意义。

点击此处了解更多信息: Value and Reference Types

答案 6 :(得分:0)

C#使用对象的引用类型。

1)在第一个示例中,您将添加一对键和一个作为引用类型的值。引用指向null,因此您实际添加(key,null)。

2)现在您已经创建了一个对象的实例。 val正在引用该对象,当您为其指定test [key] = val时,实际上是在指定val引用的对象。

3)字典中引用的对象和val变量是相同的。所以你正在访问同一个对象。

4)您已将val设置为null,因此它不会引用任何内容,但在字典中它仍然引用您创建的旧对象。