Есть несколько подходов к этому. самым простым было бы добавить свойство прокладки, т.е.
public Foo Foo { get; private set; }
[ProtoMember(1)]
private SomeBasicEnum FooSerialization {
/* shim between Foo and SomeBasicEnum in get/set */
}
Однако, если у вас много Foo
свойств, это может быть проблемой. Поэтому вместо этого v2 предлагает «суррогатные» типы, то есть, если он может найти оператор преобразования между двумя типами, он с радостью заменит их для вас автоматически. В этом случае мы хотели бы переключиться на перечисление, и, поскольку вы не можете добавлять операторы к перечислениям, вам нужно добавить оператор к Foo
:
public static implicit operator Foo(FooSurrogate value)
{
switch (value)
{
case FooSurrogate.Nil: return null;
case FooSurrogate.Instance1: return Foo.Instance1;
case FooSurrogate.Instance2: return Foo.Instance2;
default: throw new InvalidEnumArgumentException("value");
}
}
public static implicit operator FooSurrogate(Foo value)
{
if (value == null) return FooSurrogate.Nil;
if (value == Foo.Instance1) return FooSurrogate.Instance1;
if (value == Foo.Instance2) return FooSurrogate.Instance2;
throw new InvalidEnumArgumentException("value");
}
и где-то есть простое перечисление:
public enum FooSurrogate
{
Nil, Instance1, Instance2
}
И настройте его (где-то при запуске приложения):
RuntimeTypeModel.Default.Add(typeof(Foo), false).SetSurrogate(
typeof(FooSurrogate));
И мы готовы идти. Также требуется незначительная настройка, потому что в Bar
отсутствует конструктор без параметров; 2 варианта здесь:
- добавьте
private Bar() {}
, который он может использовать
- явно скажите ему не использовать конструктор:
[ProtoContract(SkipConstructor = true)]
Добавьте тестовую установку:
static void Main()
{
RuntimeTypeModel.Default.Add(typeof(Foo), false).SetSurrogate(
typeof(FooSurrogate));
var obj = new Bar(Foo.Instance1);
var clone = Serializer.DeepClone(obj);
bool same = ReferenceEquals(obj.Foo, clone.Foo);
Debug.Assert(same); // passes
}
Вероятно, я мог бы также сделать возможным использование внешних методов, похожих на операторы, чтобы избежать необходимости иметь оператор на Foo
, что немного уродливо.
Последним вариантом для меня было бы добавить поддержку IObjectReference
, но, честно говоря (и особенно в этом случае), использование базового перечисления для реализации аккуратнее и эффективнее.
person
Marc Gravell
schedule
28.10.2011