C# 为什么单例不能直接实现IObjectReference?

C# 为什么单例不能直接实现IObjectReference?,c#,.net,serialization,singleton,C#,.net,Serialization,Singleton,每当有人问如何在C#中实现可序列化的单例时,基本建议总是实现ISerializable,然后在GetObjectData中将该类型设置为实现IObjectReference的帮助器类型。然后该类型的GetRealObject函数应该返回singleton实例 在本页的示例代码中实际上就是这样做的: 我的问题是为什么没有人建议singleton本身实现IObjectReference?它在某些情况下不应该工作吗 例如,考虑这一点: using System; using System.Colle

每当有人问如何在C#中实现可序列化的单例时,基本建议总是实现ISerializable,然后在GetObjectData中将该类型设置为实现IObjectReference的帮助器类型。然后该类型的GetRealObject函数应该返回singleton实例

在本页的示例代码中实际上就是这样做的:

我的问题是为什么没有人建议singleton本身实现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之前,第二个实例确实存在。但这段时间肯定很短,不重要,对吧

  • 很可能是因为反序列化“真实”单例类型的实例将要求临时存在多个单例实例,这将违反其基本设计原则

    由于单身人士往往很重,这样做可能会导致实际问题。例如,如果单例在其构造函数中打开一个文件以缓存内容,则临时的第二个单例可能会再次尝试打开同一个文件,从而导致异常

    在使用
    BinaryFormatter
    进行序列化的特定情况下,序列化“真实”单例将导致序列化其所有内部状态(即所有公共和私有字段)。这可能不是我们想要的,因为单例通常表示全局会话状态而不是模型状态。要避免内部状态的序列化,需要标记所有字段,这些字段很容易被忽略


    这可能是因为你的单身汉没有上述任何问题,但其他人可能会有,所以官方建议不应该建议这样做。相反,建议使用更复杂的设计模式,只要您已经验证这样做不会导致上述问题,就可以简化自己。

    您将公开
    IObjectReference
    实现;也许这就是为什么推荐是一个嵌套类。任何人推荐的几率乘以任何人推荐的几率都会留下一个相当低的数字。可能是因为它需要暂时存在多个singleton实例,这将违反其基本设计。单身汉通常很重。因此,这样做可能会导致问题。例如,如果单例在其构造函数中打开一个文件以缓存内容,则临时的第二个单例可能会再次尝试打开同一个文件,从而导致异常。现在,可能是您的特定单身汉没有这个问题,但其他人可能有,所以官方建议不建议这样做。@dbc这听起来是一个很好的理由。我没有考虑过沉重的单身汉。如果你把它作为一个答案,我会接受的——当然,除非有更好的答案出现。