惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
The Exploit Database - CXSecurity.com
A
Arctic Wolf
K
Kaspersky official blog
T
Threat Research - Cisco Blogs
PCI Perspectives
PCI Perspectives
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Privacy International News Feed
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
U
Unit 42
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
P
Privacy & Cybersecurity Law Blog
O
OpenAI News
量子位
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
Cisco Blogs
AWS News Blog
AWS News Blog
Vercel News
Vercel News
Microsoft Security Blog
Microsoft Security Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
美团技术团队
T
Threatpost
S
Schneier on Security
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
C
Cyber Attacks, Cyber Crime and Cyber Security
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes
Blog — PlanetScale
Blog — PlanetScale
C
Cybersecurity and Infrastructure Security Agency CISA
F
Full Disclosure
博客园_首页
N
Netflix TechBlog - Medium
Security Latest
Security Latest
有赞技术团队
有赞技术团队
Google DeepMind News
Google DeepMind News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Register - Security
The Register - Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Recent Announcements
Recent Announcements
博客园 - Franky
P
Palo Alto Networks Blog
Project Zero
Project Zero
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
H
Help Net Security
Hacker News: Ask HN
Hacker News: Ask HN
Cisco Talos Blog
Cisco Talos Blog
H
Heimdal Security Blog
The Hacker News
The Hacker News
博客园 - 【当耐特】
GbyAI
GbyAI

博客园 - AndyHai

用C#实现的黑客帝国中的字符雨特效 一个PCM音频转换与混音的示例 关心则乱 让ASPX和ASMX脱离IIS运行的例子(ASP.NET宿主程序) SQL 中如何对纪录进行拆分 带有空值提示的TextBox NAT类型检测方法(转载) 在.NET中探测U盘的插入/拔出 用WebService实现中国移动的Provision反向接口 一个动态加载/卸载DLL的例子 用ASP.NET调用Tuxedo Tuxedo 搞定! 用Multi-Media Library实现的波形音频录制与播放 用Multi-Media Library制作流式音频播放器 研究如何用Multi-Media Library播放波形数据 又多一道面试题 面试 RTP协议
谁动了我的构造函数?
AndyHai · 2008-07-26 · via 博客园 - AndyHai

谁动了我的构造函数?

——由DBNull引发的……

  总所周知,DBNull只有一个实例——DBNull.Value,我们不可能通过new DBNull()方法来创建一个新的DBNull实例,这是因为:DBNull的构造函数是私有的,大概就如同下面这样。

public sealed class DBNull
{
    
public static readonly DBNull Value = new DBNull();
    
private DBNull()
    {
    }
}

一个典型的单实例模式,那么也就是说,两个非null的DBNull对象实例间的==比较,一定会返回true咯?看起来……似乎……确实是这样的,因为这个对象只有一个实例嘛,它们的引用比较一定是相等的。

慢着,在下这个结论前,先给大家看一段代码

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.IO;namespace ConsoleApplication2008
{
    
public class Program
    {
public class TestClass
        {
            [XmlElement()]
            
public DBNull MyValue = DBNull.Value;
        }
static void Main(string[] args)
        {
            TestClass v1 
= new TestClass();

            XmlSerializer xs 

= new XmlSerializer(typeof(TestClass));
            MemoryStream ms 
= new MemoryStream();
            xs.Serialize(ms, v1);
            
//string xml = Encoding.UTF8.GetString(ms.GetBuffer());
            
//Console.WriteLine(xml);// 此处两行可要可不要

            ms.Seek(
0, SeekOrigin.Begin);
            TestClass v2 
= xs.Deserialize(ms) as TestClass;

            Console.WriteLine(v1.MyValue 

== v2.MyValue ? "Match" : "Unmatch");

            Console.ReadKey();
        }
    }
}

  没什么花哨的地方,无非是xml序列化,然后再反序列化而已,运行一次看看……Oh,为什么?结果怎么会是Unmatch?难道说DBNull对象可以有两个不同的实例?这和它的声明可不一致啊!它的构造函数可是private的,除了它自身,不应该有其它对象可以调用的,难道说……
  整理一下思路,TestClass对象在反序列化时,到底发生了什么?当然是调用TestClass的构造函数,生成一个实例咯,然后呢?调用每个公共成员(属性和成员变量)的类型的构造函数,将实例赋给相应的成员,如此反复下去……咦?等等,DBNull的构造函数是private的,要想取得实例,必须使用DBNull.Value来获得,.NET框架不可能聪明到会自动去使用DBNull.Value吧?既然这样,那DBNull是如何被反序列化出实例来的?一个私有的构造函数是怎么会被调用的?

  不妨再做个例子:

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.IO;namespace ConsoleApplication2008
{
    
public class Program
    {
        
public sealed class SingletoneClass
        {
            
private static int i = 0;
            
public static readonly SingletoneClass Value = new SingletoneClass();private SingletoneClass()
            {
                Console.WriteLine(
"SingletoneClass 被第 {0} 次实例化"++i);
            }
        }
public class TestClass
        {
            [XmlElement()]
            
public SingletoneClass MyValue = SingletoneClass.Value;
        }
static void Main(string[] args)
        {
            Console.WriteLine(
"构造TestClass.");
            TestClass v1 
= new TestClass();

            XmlSerializer xs 

= new XmlSerializer(typeof(TestClass));
            MemoryStream ms 
= new MemoryStream();
            Console.WriteLine(
"v1序列化.");
            xs.Serialize(ms, v1);

            ms.Seek(

0, SeekOrigin.Begin);
            Console.WriteLine(
"反序列化出v2.");
            TestClass v2 
= xs.Deserialize(ms) as TestClass;

            Console.WriteLine(v1.MyValue 

== v2.MyValue ? "Match" : "Unmatch");

            Console.ReadKey();
        }
    }
}

哦~运行结果是这样:

构造TestClass.
SingletoneClass 被第 
1 次实例化
v1序列化
.
反序列化出v2
.
SingletoneClass 被第 
2 次实例化
Unmatch

SingletoneClass的第1次实例化很容易理解,那是它自身的静态只读成员Value造成的,第二次是什么原因造成的呢?想来想去,只能是在反序列化过程中调用的,也就是说“.NET框架可以调用我们的私有构造函数”。那么问题就产生了,我期望的只有一个实例的对象现在出现了两个实例,这样的话==操作符判断的结果就肯定会是false,如果你的代码中出现了大量的:

if (v == DBNull.Value)
{
}


这样的代码,而你又对这个对象做了XML序列化/反序列化,那么很显然,这段代码不能如我们所期望的那样工作。怎么办?

如果是如SingletoneClass一样的自定义对象,那么也好解决,重载==操作符即可:

public sealed class SingletoneClass
{
    
private static int i = 0;
    
public static readonly SingletoneClass Value = new SingletoneClass();private SingletoneClass()
    {
        Console.WriteLine(
"SingletoneClass 被第 {0} 次实例化"++i);
    }
public override bool Equals(object obj)
    {
        
if (obj == null || obj.GetType() == this.GetType())
            
return true;
        
else
            
return false;
    }
public static bool operator ==(SingletoneClass a, SingletoneClass b)
    {
        
// If both are null, or both are same instance, return true.
        if (System.Object.ReferenceEquals(a, b))
            
return true;return a.Equals(b);
    }
public static bool operator !=(SingletoneClass a, SingletoneClass b)
    {
        
return !(a == b);
    }
}

  这样做虽然有违==操作符的本意,却也是无可奈何的解决办法,你总不能拒绝.NET框架调用你的私有构造函数吧?那样的话,反序列化又如何进行呢?不过DBNull又该如何解决呢?我们不能通过修改.NET框架本身来达到我们的目的吧?(或者让微软出补丁?这算是BUG吗?)哪位看官能帮我解答?