最近在写一个小工具,里面用到了一个自定义的类,并且需要对该类进行多个实例化。
因为需要根据需求来取不同的实例,所以决定将其放置到一个字典中,以便取用。
另外,由于可能之后会改动实例化时的内容,所以准备将具体实例化的代码封装到一个单独的子程序中,以便更改。
所以写了如下的代码:
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实际也访问的是同样的地址,所以字典内的内容也会随之改变。
这样说起来可能有点乱,用图来表示应该会明了一些。
以上均为本人理解,如有疏漏还请各位多多指教。