我在通过 DataContract 序列化和反序列化单例时遇到问题。
首先是一些事实:
1.) Singleton is "internal"
2.) Singleton contains Dictionaries
我的序列化和反序列化工作正常,但这不是单例的正确方法。如果我反序列化 xml,我总是会生成我的单例的新实例并覆盖单例对象的当前引用 - 但在此之后它不再是单例。
有人知道吗?- 谢谢。
我在通过 DataContract 序列化和反序列化单例时遇到问题。
首先是一些事实:
1.) Singleton is "internal"
2.) Singleton contains Dictionaries
我的序列化和反序列化工作正常,但这不是单例的正确方法。如果我反序列化 xml,我总是会生成我的单例的新实例并覆盖单例对象的当前引用 - 但在此之后它不再是单例。
有人知道吗?- 谢谢。
从 msdn检查此链接,有一个关于序列化单例的示例。反序列化后,您应该返回引用而不是对象。
(.net 4.0 的代码)
我有同样的问题:反序列化需要创建单例类的新实例,它可以(!)这样做,因为它在成员函数中:构造函数对成员可见,但该实例不能替换单例实例从外部可见(“this”)。
因此,您必须将反序列化实例中的属性复制到“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);
}
尝试使用 NetDataContractSerializer-
NetDataContractSerializer 在一个重要方面与 DataContractSerializer 不同:NetDataContractSerializer 在序列化的 XML 中包含 CLR 类型信息,而 DataContractSerializer 不包含。因此,只有在序列化和反序列化端共享相同的 CLR 类型时,才能使用 NetDataContractSerializer。
序列化程序可以序列化已应用 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);
我刚刚为原子类搜索了很长时间的类似解决方案,并找到了Marc Gravell的类似问题的答案。这也适用于单身人士。
在您的单例类中,您实现由接口 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)调用它。
[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;
}
}