2020-06-25

【C#】关于字典存储不同类型变量的效果

最近在写一个小工具,里面用到了一个自定义的类,并且需要对该类进行多个实例化。

因为需要根据需求来取不同的实例,所以决定将其放置到一个字典中,以便取用。

另外,由于可能之后会改动实例化时的内容,所以准备将具体实例化的代码封装到一个单独的子程序中,以便更改。

 

所以写了如下的代码:

 1 namespace Example 2 { 3  public partial class MainWindow : Window 4  { 5   //因为在其他地方会引用到,所以放在最外层定义 6   public static MyClass staff1;  7   public static MyClass staff2; 8   public static Dictionary<string, MyClass> staffDic = new Dictionary<string, MyClass>() 9   {10    {"aaa",staff1},11    {"bbb",staff2}12   };13 14   private void Window_Loaded(object sender, RoutedEventArgs e)15   {16    InitializationStaff();17    System.Console.WriteLine(staffDic["aaa"]);18   }19 20   private void InitializationStaff() //给变量添加实例21   {22    staff1=new MyClass(){a=1,b=2,c=3};23    staff2=new MyClass(){a=3,b=2,a=1};24   }25  }26 27  Class MyClass 28  {29   public int a;30   public int b;31   public int c;32  }33 }

结果发现,输出的时候报错了,提示在字典中该项对应的内容为Null。

而如果将代码修改一下,在外面先实例化一下:

namespace Example{ public partial class MainWindow : Window {  //因为在其他地方会引用到,所以放在最外层定义  //在定义变量时就实例化  public static MyClass staff1 = new MyClass();   public static MyClass staff2 = new MyClass();  public static Dictionary<string, MyClass> staffDic = new Dictionary<string, MyClass>()  {   {"aaa",staff1},   {"bbb",staff2}  };  private void Window_Loaded(object sender, RoutedEventArgs e)  {   InitializationStaff();   System.Console.WriteLine(staffDic["aaa"]);  }  private void InitializationStaff() //给变量修改内容  {   staff1.a = 1;   staff1.b = 2;   staff1.c = 3;   staff2.a = 3;   staff2.b = 2;   staff2.a = 1;  } } Class MyClass  {  public int a;  public int b;  public int c; }}

此时,输出的内容就正常了。

那么究竟这背后发生了什么呢?

 

在请教过朋友后了解到,在C#中,为字典添加key对应的value时,实际上发生的效果是将目标内容复制到字典中,而并非是引用

了解过变量和内存相关知识的人应该知道,当我们写下一行代码:

int a = 123;

此时发生的是,系统首先找到一块内存空间,把123这个值存储到其中,并记录该内存空间的地址A。

而在变量a中,实际存储的内容就是地址A。

 

当我们将变量添加至字典中时:

Dictionary<string, int> Dic = new Dictionary<string, int>() { { "number1", a } };

字典首先找到a这个变量,得到了存储着数据的地址A。

随后将地址A中的内容复制,并粘贴到字典开辟出的另一块内存空间中。而这个内存空间的地址是地址B。

此时,无论我们给变量a如何赋值,字典中的a是不会改变的。

因为在给变量a赋值时,实际修改的是地址为A的内存空间中的数据,而字典中的a存储在的位置是地址为B的内存空间中。

 

可是,如果按照这个结论来看,在本文开头部分我用的第二种方法,也应该是在外部修改了类中成员的数据后,字典中的内容不变啊?

那为什么在外部修改的时候字典内的内容也改变了呢?

 

当我们写下以下代码的时候:

 1 namespace Example 2 { 3  public partial class MainWindow : Window 4  { 5   private void Window_Loaded(object sender, RoutedEventArgs e) 6   { 7    public static MyClass staff = new MyClass();  8   } 9  }10 11  Class MyClass 12  {13   public int a;14   public int b;15   public int c;16  }17 }

变量staff中,存储了一个地址A,地址A处存储了MyClass这样的一个类型,也就是a、b和c三个变量。

而a、b和c三个变量,实际上是分别存储了地址a,地址b,地址c。

这三个地址所指向的地方,才是各自存储了数据的内存空间。

 

当我们将staff添加到字典中时,字典读取到了地址A,并将地址A处存储的内容复制到了自己新开辟的、在地址B处的内存空间中。

在复制的时候,a、b、c三个变量的地址也就随着被复制到了地址B处中。

 

这个时候,外部的变量staff和字典中的staff,都会指向同样的三块内存空间。

当通过外部变量staff修改内容时,由于字典内的staff实际也访问的是同样的地址,所以字典内的内容也会随之改变。

 

这样说起来可能有点乱,用图来表示应该会明了一些。

 

以上均为本人理解,如有疏漏还请各位多多指教。

 

【C#】关于字典存储不同类型变量的效果

No comments:

Post a Comment