编辑:这应该从下一个版本开始工作,只需标记类型AsReferenceDefault
:
[ProtoContract(AsReferenceDefault=true)]
public class A
{
// ...
}
目前,这是一种不受支持的场景——至少,通过它不受支持的属性;基本上,AsReference=true
current指的是KeyValuePair<int,A>
,这实际上没有意义,因为KeyValuePair<int,A>
它是一个值类型(因此永远不能将其视为参考;我在本地副本中为此添加了更好的消息)。
因为KeyValuePair<int,A>
(默认情况下)充当元组,所以目前没有地方支持该AsReference
信息,但这是我希望更好地支持的场景,我将对此进行调查。
还有一个错误意味着元组(甚至AsReference
是引用类型的元组)出现乱序,但我已经在本地修复了它;这就是“更改”消息的来源。
理论上,我做这件事的工作量并不大。基本原理已经起作用了,奇怪的是昨晚也在推特上单独出现了——我猜“字典指向一个对象”是一个非常常见的场景。猜测一下,我想我会添加一些属性来帮助描述这种情况,但你现在实际上可以使用几种不同的路线来解决它:
1:KeyValuePair<int,A>
手动配置:
[Test]
public void ExecuteHackedViaFields()
{
// I'm using separate models **only** to keep them clean between tests;
// normally you would use RuntimeTypeModel.Default
var model = TypeModel.Create();
// configure using the fields of KeyValuePair<int,A>
var type = model.Add(typeof(KeyValuePair<int, A>), false);
type.Add(1, "key");
type.AddField(2, "value").AsReference = true;
// or just remove AsReference on Items
model[typeof(B)][2].AsReference = false;
Execute(model);
}
我不太喜欢这个,因为它利用了KeyValuePair<,>
(私有字段)的实现细节,并且可能无法在 .NET 版本之间工作。我宁愿通过代理即时替换 :KeyValuePair<,>
[Test]
public void ExecuteHackedViaSurrogate()
{
// I'm using separate models **only** to keep them clean between tests;
// normally you would use RuntimeTypeModel.Default
var model = TypeModel.Create();
// or just remove AsReference on Items
model[typeof(B)][2].AsReference = false;
// this is the evil bit: configure a surrogate for KeyValuePair<int,A>
model[typeof(KeyValuePair<int, A>)].SetSurrogate(typeof(RefPair<int, A>));
Execute(model);
}
[ProtoContract]
public struct RefPair<TKey,TValue> {
[ProtoMember(1)]
public TKey Key {get; private set;}
[ProtoMember(2, AsReference = true)]
public TValue Value {get; private set;}
public RefPair(TKey key, TValue value) : this() {
Key = key;
Value = value;
}
public static implicit operator KeyValuePair<TKey,TValue>
(RefPair<TKey,TValue> val)
{
return new KeyValuePair<TKey,TValue>(val.Key, val.Value);
}
public static implicit operator RefPair<TKey,TValue>
(KeyValuePair<TKey,TValue> val)
{
return new RefPair<TKey,TValue>(val.Key, val.Value);
}
}
这配置了要使用的东西,而不是 KeyValuePair<int,A>
(通过运算符转换)。
在这两者中,Execute
只是:
private void Execute(TypeModel model)
{
A a = new A();
B b = new B();
b.A = a;
b.Items.Add(1, a);
Assert.AreSame(a, b.A);
Assert.AreSame(b.A, b.Items[1]);
B deserializedB = (B)model.DeepClone(b);
Assert.AreSame(deserializedB.A, deserializedB.Items[1]);
}
但是,我确实想添加直接支持。上述两个方面的好处是,当我有时间这样做时,您只需删除自定义配置代码。
为了完整起见,如果您的代码正在使用方法,那么您应该配置默认Serializer.*
模型,而不是创建/配置新模型:
RuntimeTypeModel.Default.Add(...); // etc
Serializer.*
基本上是一个捷径RuntimeTypeModel.Default.*
。
TypeModel
最后:你不应该在每次调用时创建一个新的;那会损害性能。您应该创建和配置一个模型实例,并多次重复使用它。或者只使用默认模型。