随笔 - 34  文章 - 0 评论 - 1130 阅读 - 28万
< 2025年3月 >
23 24 25 26 27 28 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 31 1 2 3 4 5

   我的上一篇随笔<.NET Framework源码研究系列之---Delegate>中提到.NET事件的一个Bug.今天整理出来跟大家一起分享一下,顺便请大家说说自己的看法.

      不说别的,先看测试代码:    

复制代码
代码
1 using System;
2  using System.IO;
3  using System.Runtime.Serialization;
4  using System.Runtime.Serialization.Formatters.Soap;
5
6  namespace OneBug
7 {
8 class Program
9 {
10 static void Main(string[] args)
11 {
12 try
13 {
14 TestItem tt = new TestItem();
15 UserClass us = new UserClass(tt);
16 tt.Invoke();
17 using (Stream stream = new FileStream("MyFile.bin", FileMode.Create, FileAccess.Write, FileShare.None))
18 {
19 IFormatter formatter = new SoapFormatter();
20 formatter.Serialize(stream, tt);
21 stream.Close();
22 }
23 Console.Read();
24 }
25 catch (Exception ex)
26 {
27 Console.WriteLine(ex.Message);
28 }
29 }
30 }
31 [Serializable]
32 public class TestItem
33 {
34 public int _Field1 = 10;
35 public event EventHandler OnChange;
36 public TestItem() { }
37 public void Invoke()
38 {
39 if (OnChange != null)
40 OnChange(this, null);
41 }
42 }
43 //[Serializable]
44   public class UserClass
45 {
46 public string _UserName = "倪大虾";
47 public TestItem _Item;
48 public UserClass(TestItem item)
49 {
50 _Item = item;
51 item.OnChange += new EventHandler(item_OnChange);
52 }
53 void item_OnChange(object sender, EventArgs e)
54 {
55 Console.WriteLine(this._UserName);
56 }
57 }
58 }
复制代码

上面一段代码看起来没有问题,但是编译的时候会有如下错误:

如果把44行前面的"//"去掉,则没有问题,反序列化成功.这时我们打开序列化的文件看下里面的内容,如下:

<a1:TestItem id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/OneBug/OneBug%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<_Field1>20</_Field1>
<OnChange href="#ref-3"/>
</a1:TestItem>

.....
<a1:UserClass id="ref-5" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/OneBug/OneBug%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<_UserName id="ref-13">倪大虾</_UserName>
</a1:UserClass>

是不是觉得很神气呢,对象tt序列化的时候居然连同对象us一同序列化了.

 

到这里问题就很明白了,至少看起来应该是UserClass的实例us保存有TestItem的实例tt的一个引用.就算序列化时需要将一个实例的引用对象一起序列化,那也应该是序列化us时出现的现象(事实却是如此),而不应该是此类状况.看下面的测试代码:

复制代码
代码
1 using System;
2  using System.IO;
3  using System.Runtime.Serialization;
4  using System.Runtime.Serialization.Formatters.Soap;
5
6  namespace OneBug{
7 class Program{
8 static void Main(string[] args){
9 try{
10 Class2 c2 = new Class2();
11 using (Stream stream = new FileStream("MyFile21.bin", FileMode.Create, FileAccess.Write, FileShare.None))
12 {
13 IFormatter formatter = new SoapFormatter();
14 formatter.Serialize(stream, c2.c1);
15 stream.Close();
16 }
17 Console.Read();
18 }
19 catch (Exception ex){
20 Console.WriteLine(ex.Message);
21 }
22 }
23 }
24 [Serializable]
25 public class Class1 {
26 public int f1 = 1000;
27 }
28 [Serializable]
29 public class Class2 {
30 public string s1 = "niyw";
31 public Class1 c1 = new Class1();
32 }
33 }
复制代码

此段代码中c2.c1的序列化结果为

<SOAP-ENV:Body>
<a1:Class1 id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/OneBug/TwoBug%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<f1>1000</f1>
</a1:Class1>

丝毫没有Class2的实体c2的影子.

 

  至此,显然我们发现了一个问题,那就是为什么tt的序列化文件中会包含us的序列化内容?

     难道是微软的Bug吗?!看起来像.不过联系<.NET Framework源码研究系列之---Delegate>中的观点,我们就不难理解这个现象了.TestItem的实例tt中包含了一个事件(Event)OnChange,而这个事件包含了UserClass的实例us的引用.所以在序列化tt的时候连同us一起序列化了.这点也可以从序列化文件中看出端倪,我从中找出了如下内容:

....

<a2:DelegateSerializationHolder id="ref-5" xmlns:a2="http://schemas.microsoft.com/clr/ns/System">
<Delegate href="#ref-6"/>
<target0 href="#ref-1"/>
<method0 href="#ref-7"/>
</a2:DelegateSerializationHolder>
<a2:DelegateSerializationHolder_x002B_DelegateEntry id="ref-6" xmlns:a2="http://schemas.microsoft.com/clr/ns/System">
<type id="ref-8">System.EventHandler</type>
<assembly id="ref-9">mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</assembly>
<target id="ref-10" xsi:type="SOAP-ENC:string">target0</target>
<targetTypeAssembly id="ref-11">TwoBug, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</targetTypeAssembly>
<targetTypeName id="ref-12">OneBug.UserClass</targetTypeName>
<methodName id="ref-13">item_OnChange</methodName>
<delegateEntry xsi:null="1"/>

 ... 

  随便写到此处,相比大家明白了文章一开始出现的错误,和对.NET的委托有更深入的了解.

 

  但是,此处仍有问题让我想不明白.那就是如果我们对tt进行反序列化获得一个TestItem的实例,其中定然有一个UserClass的实例,那么我们如何获取到它呢?如果获取了,又有什么意义呢? 有机会大家一起研究,今天就到此结束了.:)

posted on   倪大虾  阅读(2230)  评论(21编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示