你可以使用protobuf-net的对象枚举吗?

时间:2011-10-27 21:26:06

标签: .net protobuf-net

我们有以下内容..

public class Foo
{
   public string Name { get; private set;}

   private Foo(string name)
   {
      Name = name;
   }

   public Foo Instance1 = new Foo("Hello");
   public Foo Instance2 = new Foo("World");
}

然后引用这个我们就有了..

[ProtoContract]
public class Bar 
{
   [ProtoMember(1)]
   public Foo Foo { get; private set; }

   public Bar(Foo foo)
   {
      Foo = foo;
   }
}

所以当我反序列化一个Bar时,我需要它来获取对Foo.Instance1或Foo.Instance2的引用..

问题是我可以这样做,如果是这样的话?

1 个答案:

答案 0 :(得分:2)

有几种方法可以解决这个问题。 最简单的将添加一个shim属性,即

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
}

并配置它(在app-startup的某个地方):

RuntimeTypeModel.Default.Add(typeof(Foo), false).SetSurrogate(
                   typeof(FooSurrogate));

我们很高兴。还需要一个小调整,因为Bar缺少无参数构造函数;这里有2个选项:

  1. 添加可以使用的private Bar() {}
  2. 明确告诉它不要使用构造函数:[ProtoContract(SkipConstructor = true)]
  3. 添加测试台:

    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支持,但坦率地说(特别是在这种情况下),使用基本枚举来实现更整洁,更有效。