我想序列化和反序列化包含在字典中的对象,实现Equals
方法并在其中包含字典。但是,它会导致ArgumentException
,因为在反序列化时调用Equals
方法为时过早。
using System.Collections.Generic;
using System.Runtime.Serialization;
using NUnit.Framework;
[Serializable]
public class SerializableObject
{
public Dictionary<string, bool> Values = new Dictionary<string, bool>();
public override bool Equals(object obj)
{
if (!(obj is SerializableObject other))
return false;
if (ReferenceEquals(this, obj))
return true;
if (Values.Count != other.Values.Count)
return false;
foreach (var (k, v) in Values)
if (!other.Values.TryGetValue(k, out var otherV) || v != otherV)
return false;
return true;
}
public override int GetHashCode()
{
return 0;
}
}
[Test]
public void DeserializeDictionary()
{
using (var stream = new MemoryStream())
{
var obj1 = new SerializableObject();
obj1.Values["aaa"] = true;
var obj2 = new SerializableObject();
obj2.Values["bbb"] = false;
var formatter = new BinaryFormatter();
var dict = new Dictionary<SerializableObject, bool>
{
[obj1] = true,
[obj2] = false
};
formatter.Serialize(stream, dict);
stream.Seek(0, SeekOrigin.Begin);
dict = (Dictionary<SerializableObject, bool>)formatter.Deserialize(stream);
Assert.Contains(obj1, dict.Keys);
Assert.Contains(obj2, dict.Keys);
}
}
反序列化时,在两个具有空字典的对象上调用Equals
方法,并且发生ArgumentException
(“已经添加了具有相同键的项”)。
此外,如果对象实现了[OnDeserialized]
方法,则在字典仍然为空且内部字典在稍后返回整个顶级字典之前填充时,也会调用此方法。
编辑:在这个简短的示例中,GetHashCode
故意返回0以强制调用Equals
。在实际程序中,只有两个不等的对象具有相同的哈希码(冲突)。
编辑2 :这是一个嵌套对象的更复杂的示例。
[Serializable]
public class SerializableObject : ISerializable
{
public SerializableObject() {}
public Dictionary<string, bool> Values = new Dictionary<string, bool>();
public override bool Equals(object obj)
{
if (!(obj is SerializableObject other))
return false;
if (ReferenceEquals(this, obj))
return true;
if (Values.Count != other.Values.Count)
return false;
foreach (var kvp in Values)
if (!other.Values.TryGetValue(kvp.Key, out var otherV) || kvp.Value != otherV)
return false;
return true;
}
public override int GetHashCode() => 0;
public void GetObjectData(SerializationInfo info, StreamingContext context) =>
info.AddValue(nameof(Values), Values.ToArray());
private SerializableObject(SerializationInfo info, StreamingContext context) =>
Values = new Dictionary<string, bool>(info.Get<KeyValuePair<string, bool>[]>(nameof(Values)));
}
[Serializable]
public class SerializableObject2 : ISerializable
{
public SerializableObject2() { }
public Dictionary<SerializableObject, bool> Values = new Dictionary<SerializableObject, bool>();
public override bool Equals(object obj)
{
if (!(obj is SerializableObject2 other))
return false;
if (ReferenceEquals(this, obj))
return true;
if (Values.Count != other.Values.Count)
return false;
foreach (var kvp in Values)
if (!other.Values.TryGetValue(kvp.Key, out var otherV) || kvp.Value != otherV)
return false;
return true;
}
public override int GetHashCode() => 0;
public void GetObjectData(SerializationInfo info, StreamingContext context) =>
info.AddValue(nameof(Values), Values.ToArray());
private SerializableObject2(SerializationInfo info, StreamingContext context) =>
Values = new Dictionary<SerializableObject, bool>(info.Get<KeyValuePair<SerializableObject, bool>[]>(nameof(Values)));
}
[Test]
public void DeserializeDictionary()
{
using (var stream = new MemoryStream())
{
var obj1 = new SerializableObject();
obj1.Values["aaa"] = true;
var obj2 = new SerializableObject();
obj2.Values["bbb"] = false;
var sobj1 = new SerializableObject2
{
Values = new Dictionary<SerializableObject, bool>
{
[obj1] = true,
[obj2] = false
}
};
var formatter = new BinaryFormatter();
formatter.Serialize(stream, sobj1);
stream.Seek(0, SeekOrigin.Begin);
sobj1 = (SerializableObject2)formatter.Deserialize(stream);
Assert.Contains(obj1, sobj1.Values.Keys);
Assert.Contains(obj2, sobj1.Values.Keys);
}
}
答案 0 :(得分:2)
我不确定,只是在推测,但是直到SerializableObject
被序列化(在本例中为Dictionary
之后,dict
似乎才被填充。 )添加了它们。
似乎您可以通过SerializableObject
实现ISerializable
并提供自己的GetObjectData
和反序列化构造函数来解决此问题。
[Serializable]
public class SerializableObject : ISerializable
{
public SerializableObject()
{
}
public Dictionary<string, bool> Values = new Dictionary<string, bool>();
public override bool Equals(object obj)
{
if (!(obj is SerializableObject other))
return false;
if (ReferenceEquals(this, obj))
return true;
if (Values.Count != other.Values.Count)
return false;
foreach (var kvp in Values)
if (!other.Values.TryGetValue(kvp.Key, out var otherV) || kvp.Value != otherV)
return false;
return true;
}
public override int GetHashCode()
{
return 0;
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue(KeyValuePairsKey, Values.Select(kvp => kvp).ToArray());
}
private SerializableObject(SerializationInfo info, StreamingContext context)
{
var kvps = info.GetValue(KeyValuePairsKey, typeof(KeyValuePair<string, bool>[])) as KeyValuePair<string, bool>[];
foreach (var kvp in kvps)
{
Values.Add(kvp.Key, kvp.Value);
}
}
private const string KeyValuePairsKey = "KVPS";
}
class Program
{
static void Main(string[] args)
{
using (var stream = new MemoryStream())
{
var obj1 = new SerializableObject();
obj1.Values["aaa"] = true;
var obj2 = new SerializableObject();
obj2.Values["bbb"] = false;
var formatter = new BinaryFormatter();
var dict = new Dictionary<SerializableObject, bool>
{
[obj1] = true,
[obj2] = false
};
formatter.Serialize(stream, dict);
stream.Seek(0, SeekOrigin.Begin);
dict = (Dictionary<SerializableObject, bool>)formatter.Deserialize(stream);
}
}
}
答案 1 :(得分:0)
您总是在GetHashCode()方法中返回0。我认为字典使用GetHashCode()来确定键的相等性。