单例不能直接实现IObjectReference的任何原因
本文关键字:任何原 IObjectReference 实现 不能 单例 | 更新日期: 2023-09-27 17:57:36
每当有人问如何在C#中实现可序列化的单例时,基本建议总是实现ISerializable,然后在GetObjectData中将类型设置为实现IObjectReference的助手类型。然后,该类型的GetRealObject函数应该返回singleton实例。
在本页的示例代码中,实际上就是这样做的:https://msdn.microsoft.com/en-us/library/system.runtime.serialization.iobjectreference.aspx
我的问题是,为什么没有人建议单例本身实现IObjectReference?它在某些情况下不应该起作用吗?
例如:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
class Program {
// Works:
[Serializable]
class Singleton1 : ISerializable {
public static readonly Singleton1 instance = new Singleton1();
private Singleton1() {
}
public void GetObjectData(SerializationInfo info, StreamingContext context) {
info.SetType(typeof(Helper));
}
[Serializable]
private class Helper : IObjectReference {
public object GetRealObject(StreamingContext context) {
return instance;
}
}
}
// Works:
[Serializable]
class Singleton2 : IObjectReference {
public static readonly Singleton2 instance = new Singleton2();
private Singleton2() {
}
public object GetRealObject(StreamingContext context) {
return instance;
}
}
// Does not work, of course:
[Serializable]
class Singleton3 {
public static readonly Singleton3 instance = new Singleton3();
private Singleton3() {
}
}
static void Main(string[] args) {
Console.WriteLine("Testing Singleton1");
TestSingleton(Singleton1.instance);
Console.WriteLine("Testing Singleton2");
TestSingleton(Singleton2.instance);
Console.WriteLine("Testing Singleton3, expect to fail.");
TestSingleton(Singleton3.instance);
}
static void TestSingleton(object singletonInstance) {
BinaryFormatter binaryFormatter = new BinaryFormatter();
MemoryStream memoryStream = new MemoryStream();
binaryFormatter.Serialize(memoryStream, singletonInstance);
memoryStream.Position = 0;
object newInstance = binaryFormatter.Deserialize(memoryStream);
bool shouldBeTrue = object.ReferenceEquals(singletonInstance, newInstance);
Debug.Assert(shouldBeTrue);
}
}
Singleton1以通常推荐的方式实现。Singleton2直接实现IObjectReference。当然,Singleton3没有做任何特别的事情,而且失败了。
我从来没有见过有人建议像上面的Singleton2那样做。为什么?
如果非要我猜测的话,我想可能是两件事之一:
- 也许是因为它在某些情况下会失败。也许理论上它将来会因为某种原因而失败
- 也许是因为在框架调用GetRealObject之前,第二个实例确实短暂存在。但这肯定是一个如此短暂的时期,它并不重要,对吧
可能是因为反序列化"真实"singleton类型的实例需要临时存在多个singleton实例,这将违反其基本设计原则。
由于单身汉往往很重,这样做可能会带来实际问题。例如,如果singleton在其构造函数中打开一个文件来缓存内容,那么临时的第二个singleton可能会尝试再次打开同一个文件,从而导致异常。
在使用BinaryFormatter
进行序列化的特定情况下,序列化"真实"的单例将导致其所有内部状态(即所有公共和私有字段)被序列化。这可能不是所需的,因为singleton通常表示全局会话状态,而不是模型状态。避免内部状态的序列化需要用[NonSerialized]
标记所有字段,这可能会成为一个容易被忽视的麻烦。
这可能是因为您的特定单身汉没有任何上述问题,但其他人可能有,所以官方建议不应该建议这样做。相反,建议使用更复杂的设计模式,只要您已经验证了这样做不会导致上述问题,您就可以简化自己。