【问题标题】:Serialize Singleton via DataContract通过 DataContract 序列化 Singleton
【发布时间】:2012-07-24 14:42:08
【问题描述】:

我在通过 DataContract 序列化和反序列化单例时遇到问题。

首先是一些事实:

1.) Singleton is "internal"
2.) Singleton contains Dictionaries

我的序列化和反序列化工作正常,但它不是单例的正确方法。如果我反序列化 xml,我总是会生成我的单例的新实例并覆盖单例对象的当前引用 - 但在此之后它不再是单例了。

有人知道吗? - 谢谢。

【问题讨论】:

  • 如果您要覆盖实例,那不是真正的单例吗? :) 那么为什么要使用单例模式呢?
  • 是的,你是对的。但我认为这是使“单身人士成为单身人士”的一种肮脏而不是通常的方式。或者是吗? :)

标签: c# singleton datacontract internal serialization


【解决方案1】:

检查这个来自 msdn 的link,有一个关于序列化单例的示例。反序列化后,您应该返回引用而不是对象。

【讨论】:

  • 这不是正确的方法 - 在我使用数据协定属性之前,我不能使用 ISerializer。我得到以下异常:类型“ConfigurationData”不能是 ISerializable 并且具有 DataContractAttribute 属性。
【解决方案2】:

尝试使用 NetDataContractSerializer-

NetDataContractSerializer 与 DataContractSerializer 不同 以一种重要方式:NetDataContractSerializer 包括 CLR 类型 序列化 XML 中的信息,而 DataContractSerializer 才不是。因此,只有在以下情况下才能使用 NetDataContractSerializer 序列化和反序列化端共享相同的 CLR 类型。

序列化程序可以将类型序列化为 DataContractAttribute 或 SerializableAttribute 属性已 应用。它还对实现 ISerializable 的类型进行序列化。

代码示例:

[DataContract(Name = "Customer", Namespace = "http://www.contoso.com")]
class Person : IExtensibleDataObject
{
    [DataMember()]
    public string FirstName;
    [DataMember]
    public string LastName;
    [DataMember()]
    public int ID;

    public Person(string newfName, string newLName, int newID)
    {
        FirstName = newfName;
        LastName = newLName;
        ID = newID;
    }

    private ExtensionDataObject extensionData_Value;    
    public ExtensionDataObject ExtensionData
    {
        get { return extensionData_Value; }
        set { extensionData_Value = value; }
    }
}

序列化:

Person p1 = new Person("Zighetti", "Barbara", 101);
FileStream fs = new FileStream(fileName, FileMode.Create);
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(fs);
NetDataContractSerializer ser = new NetDataContractSerializer();
ser.WriteObject(writer, p1);

Msdn Link here

【讨论】:

    【解决方案3】:

    (.net 4.0 的代码)

    我遇到了同样的问题: 反序列化需要创建单例类的新实例,它可以(!)这样做,因为它在成员函数内部:构造函数对成员可见,但该实例不能替换从外部可见的单例实例( “这个”)。

    因此,您必须将反序列化实例中的属性复制到“this”实例中。

    手动复制会很快变老,所以这是我使用反射复制未标记的公共可写成员的解决方案 [xmlignore]:

        public static class SerializationHelpers
        {
    
          /// <summary>
          /// Copy all public props and fields that are not xmlignore
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <param name="target"></param>
          /// <param name="other"></param>
          public static void CopyTypeFields<T>(T target, T other)
          {
    
            // get all public static properties of MyClass type
            PropertyInfo[] propertyInfos = other.GetType().GetProperties(BindingFlags.Public  
    
            | BindingFlags.Instance);
            FieldInfo[] fis = other.GetType().GetFields(BindingFlags.Public | 
            BindingFlags.Instance);
    
    
            foreach (FieldInfo fi in fis)
            {
              if ((fi.Attributes & FieldAttributes.FieldAccessMask) != 
                  FieldAttributes.Literal &&
                  (fi.Attributes & FieldAttributes.FieldAccessMask) != 
                  FieldAttributes.Static)
              {
                if (IsXmlIgnored(fi)) { continue; }
                var myval = fi.GetValue(other);
                fi.SetValue(target, myval);
              }
            }
    
    
            foreach (PropertyInfo pi in propertyInfos)
            {
              if (!pi.CanWrite || !pi.CanRead) { continue; }
              if (IsXmlIgnored(pi)) { continue; }
    
              var myval = pi.GetValue(other, null);
              pi.SetValue(target, myval, null);
            }
          }
    
          private static bool IsXmlIgnored(MemberInfo pi)
          {
            object[] fiGetCustomAttributes = pi.GetCustomAttributes(false);
            foreach (object ob in fiGetCustomAttributes)
            {
              if (ob.GetType().
                  Equals(typeof(System.Xml.Serialization.XmlIgnoreAttribute)))
              {
                return true;
              }
            }
            return false;
          }
    
        }
    
        // to use it ...
        // the deserialization method of the singleton mySingleton
    
    
    
        public bool loadSingleton()
        {
          bool ret= false;
    
          try
          {
            Type myType = GetType();
            XmlSerializer reader = new XmlSerializer(myType);
            try
            {
              using (StreamReader file = new StreamReader(filename))
              {
                try
                {
                  mySingleton t1 = (mySingleton)reader.Deserialize(file);
                  CopySerializationFields(t1);
                  ret= true;
                }
    
                catch
                {
                  ...
                }
              }
            }
            catch
            {
              ...
            }
          }
          catch (Exception ex)
          {
            ...
          }
          return ret;
        }
    
    
        private void CopySerializationFields(ProcessingSettings other)
        {
          SerializationHelpers.CopyTypeFields(this, other);
        }
    

    【讨论】:

      【解决方案4】:

      我刚刚为原子类搜索了很长时间的类似解决方案,并找到了Marc Gravellanswer to a similar problem。这也适用于单身人士。

      在您的单例类中,您实现由接口 System.Runtime.Serialization.IObjectReference 定义的 GetRealObject 方法。如果需要,您还可以在此处将先前序列化的数据添加到单例中,并将静态引用作为反序列化后使用的引用返回。

      这是我的例子:

      [System.Runtime.Serialization.DataContract]
      public class MySingletonClass : System.Runtime.Serialization.IObjectReference
      {
          private MySingletonClass()
          {
          }
      
          private static MySingletonClass _Instance;
          public static MySingletonClass Instance
          {
              get
              {
                  if (_Instance == null)
                      _Instance = new MySingletonClass();
                  return _Instance;
              }
          }
      
          object System.Runtime.Serialization.IObjectReference.GetRealObject(System.Runtime.Serialization.StreamingContext context)
          {
              MySingletonClass realObject = Instance;
              realObject.Merge(this);
              return realObject;
          }
      
          private void Merge(MySingletonClass otherInstance)
          {
              // do your merging here
          }
      }
      

      您也可以将它用于原子类。您只需将 Instance 属性更改为 GetInstance 方法,并在 GetRealObject 方法中使用适当的属性(即 ID)调用它。

      【讨论】:

        【解决方案5】:
        [DataContract]
        public sealed class SerializableSingletonPattern
        {
            public static SerializableSingletonPattern Instance { get; private set; } = new SerializableSingletonPattern();
            [DataMember] public bool YourData { get; private set; }
        
            // explicit static constructor so C# compiler will not mark type as beforefieldinit
            static SerializableSingletonPattern() { }
        
            SerializableSingletonPattern() // your constructor
            {
            }
        
            [OnDeserialized]
            void OnDeserialized(StreamingContext context)
            {
                Instance = this;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-04-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多