Struct containing reference types
结构是值类型,因此如果我将一个结构赋给另一个结构,它的字段将复制到第二个结构中。但是,如果结构的某些字段是引用类型,会发生什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public struct MyIPEndPoint { public String IP; public UInt16 Port; public MyIPEndPoint(String ipAddress, UInt16 portNumber) { IP = ipAddress; Port = portNumber; } public override string ToString() { return IP+":"+Port; } } ... static int Main(string[] args) { MyIPEndPoint address1 = new MyIPEndPoint("127.0.0.1", 8080); MyIPEndPoint address2 = address1; address2.IP ="255.255.255.255"; address2.Port = 9090; Console.WriteLine(address1); Console.WriteLine(address2); } |
输出是:
1 2 | 127.0.0.1:8080 255.255.255.255:9090 |
为什么
事实上,如果我用一个新的简单类
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class MyIP { public string IpAsString; public MyIP(string s) { IpAsString = s; } public override string ToString() { return IpAsString; } } |
当然,您还应该按以下方式调整
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public struct MyIPEndPoint { public MyIP IP; // modification public UInt16 Port; public MyIPEndPoint(String ipAddress, UInt16 portNumber) { IP = new MyIP(ipAddress); // modification Port = portNumber; } public override string ToString() { return IP+":"+Port; } } |
最后在
1 2 3 4 5 6 7 8 | MyIPEndPoint address1 = new MyIPEndPoint("127.0.0.1", 8080); MyIPEndPoint address2 = address1; address2.IP.IpAsString ="255.255.255.255"; // modification address2.Port = 9090; Console.WriteLine(address1); Console.WriteLine(address2); |
现在输出为:
1 2 | 255.255.255.255:8080 255.255.255.255:9090 |
在第一种情况下,我期待着这个输出。为什么在第一种情况下引用的行为不符合预期?
考虑你的第一个例子。你有两个抽屉,标有"地址一"和"地址二"。两个抽屉都是空的。
1 |
现在你拿到一张纸,在上面写上"127.0.0.18080",然后把它放在抽屉1里。
1 | MyIPEndPoint address2 = address1; |
现在你拿一台复印机,在"地址一"抽屉里复印一份文件,然后把复印件放在"地址二"抽屉里。
1 2 | address2.IP ="255.255.255.255"; address2.Port = 9090; |
现在,你把地址2抽屉里的那张纸划掉,换成新的文本。
抽屉一的纸没变。它仍然和以前一样。
现在考虑第二个例子。现在你有两个空抽屉,和以前一样,还有一本空白纸。
1 |
你随机挑选一页书,并将其命名为"参考书"。在那一页上写着"127.0.0.1"。你拿一张空纸,在那张纸上写上"参考号8080",然后把它贴在标有"地址一"的抽屉里。
1 | MyIPEndPoint address2 = address1; |
你在"地址一"中复印一份文件,然后把复印件放在"地址二"中。
1 | address2.IP.IpAsString ="255.255.255.255"; |
你打开抽屉"地址二",看到上面写着"参考一"。你翻阅这本书,直到找到一页写着"参考书"。你把那里的东西划掉,换成新的文字。
1 | address2.Port = 9090; |
你打开抽屉"地址二",划掉"8080",换成"9090"。你把参考号放在原处。
完成后,抽屉"地址1"包含"参考1,8080",抽屉"地址2"包含"参考1,9090",书上有一页写着"参考1:255.255.255"。
现在,您是否了解引用类型和值类型之间的区别?
您已经正确理解,对于结构,Address1和Address2不是同一对象。已复制值。但是,对于字段,这是一个简单的重新分配案例。它与字符串是引用类型、任何特殊规则或任何不变性建议无关。您只需使用另一个值重新分配属性或字段。
1 2 3 | someStruct.SomeString ="A"; anotherStruct = someStruct; anotherStruct.SomeString ="B"; // would never affect someStruct |
您已经覆盖了本例中的引用。在一小段时间内,两个结构的字段都包含相同的引用,这一事实并不重要。在第二个例子中,您做了一些非常不同的事情。
1 2 3 | someStruct.IP.SomeString ="A"; anotherStruct = someStruct; anotherStruct.IP.SomeString ="B"; |
在这种情况下,IP的值没有改变。IP的部分状态已更改。每个结构的字段仍然引用相同的IP。
简单地说
1 2 3 4 5 6 7 8 9 10 11 12 |
变量和字段保存值。对于类,这些值是对对象的引用。两个变量或字段可以保持相同的引用,但这并不意味着这些变量本身是链接的。不管怎样,它们都没有连接,它们只是持有一个共同的价值。当替换一个变量或字段的值时,另一个变量不受影响。
不是在特定的主题上,但值得注意的是可变结构被许多人视为邪恶。其他人并不完全持有相同的观点,或者至少没有那么虔诚。(然而,也值得注意的是,如果地址是一个类,那么地址1和地址2将保持相同的值(对地址对象的引用),并且只要地址1或地址2本身都没有重新分配,就可以通过地址2看到对地址1状态的修改。)
如果这是代码的实际表示,那么就值得对可变结构进行一些研究,这样至少可以充分理解可能遇到的各种陷阱。