【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实际也访问的是同样的地址,所以字典内的内容也会随之改变。

 

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

 

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

 

posted @ 2020-06-25 13:06  御琪幽然  阅读(1917)  评论(2编辑  收藏  举报