C# 如何序列化可能只有唯一实例的类

C# 如何序列化可能只有唯一实例的类,c#,serialization,C#,Serialization,我有一个类,它只能有唯一的实例,如下所示。这使得引用相等足以等同于对象 虽然序列化很简单,但反序列化有点棘手,因为不能像我在下面所做的那样分配给这个。如果仍然希望只维护类的唯一实例,如何反序列化对象 public sealed class MyClass : ISerializable, IXmlSerializable { private static readonly Dictionary<string, MyClass> Cache; stat

我有一个类,它只能有唯一的实例,如下所示。这使得引用相等足以等同于对象

虽然序列化很简单,但反序列化有点棘手,因为不能像我在下面所做的那样分配给
这个
。如果仍然希望只维护类的唯一实例,如何反序列化对象

public sealed class MyClass :
    ISerializable,
    IXmlSerializable
{
    private static readonly Dictionary<string, MyClass> Cache;

    static MyClass() { /* build cache, use private constructor */ }

    private MyClass(string name)
    {
        this.Name = name;
    }

    public string Name { get; }

    public static MyClass Parse(string from)
        => Cache[from];

    public void GetObjectData(SerializationInfo info, StreamingContext context)
        => throw new NotImplementedException();

    public XmlSchema GetSchema() => null;

    public void ReadXml(XmlReader reader)
    {
        reader.ReadStartElement();
        this = Parse(reader.ReadContentAsString());
        reader.ReadEndElement();
    }

    public MyClass(SerializationInfo info, StreamingContext context) 
        => this = Parse(info.GetString(nameof(this.Name)));

    public void WriteXml(XmlWriter writer)
        => throw new NotImplementedException();
}
公共密封类MyClass:
ISerializable,
IXmlSerializable
{
私有静态只读字典缓存;
静态MyClass(){/*构建缓存,使用私有构造函数*/}
私有MyClass(字符串名称)
{
this.Name=Name;
}
公共字符串名称{get;}
公共静态MyClass解析(来自的字符串)
=>缓存[从];
public void GetObjectData(SerializationInfo信息、StreamingContext上下文)
=>抛出新的NotImplementedException();
公共XmlSchema GetSchema()=>null;
公共void ReadXml(XmlReader)
{
reader.ReadStartElement();
this=Parse(reader.ReadContentAsString());
reader.ReadEndElement();
}
公共MyClass(SerializationInfo信息、StreamingContext上下文)
=>this=Parse(info.GetString(nameof(this.Name));
public void WriteXml(XmlWriter)
=>抛出新的NotImplementedException();
}

一般来说,处理此问题的方法是,在对象图序列化期间,使用替换单例,其中将单例替换为只包含单例标识符的。然后,稍后,当图形被反序列化时,DTO最初被反序列化,然后通过查找被相应的单例替换

例如,如果您的
MyClass
如下所示:

public sealed class MyClass
{
    private static readonly Dictionary<string, MyClass> Cache;

    static MyClass()
    {
        Cache = new Dictionary<string, MyClass>()
        {
            { "one", new MyClass("one") { OtherRuntimeData = "other runtime data 1" } },
            { "two", new MyClass("two") { OtherRuntimeData = "other runtime data 2" } },
        };
    }

    // XmlSerializer required parameterless constructor.
    private MyClass() => throw new NotImplementedException();

    private MyClass(string name) => this.Name = name;

    public string Name { get; }

    public string OtherRuntimeData { get; set; }

    public static MyClass Parse(string from) => Cache[from];

    public static IEnumerable<MyClass> Instances => Cache.Values;
}
public class RootObject
{
    // Technique taken from https://stackoverflow.com/questions/3280362/most-elegant-xml-serialization-of-color-structure
    [XmlElement(Type=typeof(MyClassDTO))]
    public MyClass MyClass { get; set; }
} 
注意在DTO和原始之间进行转换的方法?这将使将代理注入序列化图变得更容易

然而,不幸的是,在所有常用的.Net序列化程序中,都没有实现序列化代理DTO注入的标准方法。每个都有自己的独立机制(或者根本没有机制)。要分解它们:

  • 数据协定序列化程序支持通过接口使用代理进行替换,如to或to中所示

  • 数据协定序列化程序还支持到中所示的接口

  • 但不幸的是,
    XmlSerializer
    不支持通过代理机制注入DTO。最接近的方法是技巧from to,它涉及为引用单例类型的每个属性设置to DTO类型,例如:

    public sealed class MyClass
    {
        private static readonly Dictionary<string, MyClass> Cache;
    
        static MyClass()
        {
            Cache = new Dictionary<string, MyClass>()
            {
                { "one", new MyClass("one") { OtherRuntimeData = "other runtime data 1" } },
                { "two", new MyClass("two") { OtherRuntimeData = "other runtime data 2" } },
            };
        }
    
        // XmlSerializer required parameterless constructor.
        private MyClass() => throw new NotImplementedException();
    
        private MyClass(string name) => this.Name = name;
    
        public string Name { get; }
    
        public string OtherRuntimeData { get; set; }
    
        public static MyClass Parse(string from) => Cache[from];
    
        public static IEnumerable<MyClass> Instances => Cache.Values;
    }
    
    public class RootObject
    {
        // Technique taken from https://stackoverflow.com/questions/3280362/most-elegant-xml-serialization-of-color-structure
        [XmlElement(Type=typeof(MyClassDTO))]
        public MyClass MyClass { get; set; }
    } 
    
    在此处显示此操作的.Net小提琴示例:。注意,这只是因为我们前面定义的singleton和它的DTO之间的隐式运算符才起作用

  • 当通过Json.NET序列化时,单例可以替换为其内部的DTO

  • 支持通过
    RuntimeTypeModel.Default.Add(typeof(OriginalType),false)、seturrogate(typeof(代理类型))
    替换代理项,如中所示,或文章中所示

  • 最后,如果您使用的是
    BinaryFormatter
    (出于解释的原因,我不建议使用),则支持使用类似于数据契约代理替换机制的机制,如中所示

    SerializationInfo.SetType()
    还支持与
    IObjectReference
    组合使用,如中所示


如果您能够更改课程代码,我强烈建议您用三种不同的代码替换您的课程;一个用于序列化/反序列化,一个用于缓存,另一个用于需要缓存的类。你们的班级有不同的职责,这使得它更复杂。