【问题标题】:How to serialize struct which implements IEnumerable using protobuf-net and surrogate如何使用protobuf-net和代理序列化实现IEnumerable的结构
【发布时间】:2021-05-24 03:18:51
【问题描述】:

我正在使用 protobuf-net 包版本 3.0.101。

以下代码在执行typeModel.Serialize(ms, value) 时会产生运行时异常。例外是:

GenericArguments[0], 'UserQuery+Option`1[System.Int32]', on 'ProtoBuf.Serializers.RepeatedSerializer`2[TCollection,T] CreateEnumerable[TCollection,T]()' violates the constraint of type 'TCollection'.
void Main()
{
    var typeModel = RuntimeTypeModel.Create();
    typeModel.SetSurrogate<Option<int>, OptionSurrogate<int>>();
    //typeModel[typeof(Option<int>)].IgnoreListHandling = true; // This doesn't help.
    
    var value = new Option<int>(true, 5);
    
    var ms = new MemoryStream();
    typeModel.Serialize(ms, value);
}

struct Option<T> : IEnumerable<T>
{
    public Option(bool hasValue, T value)
    {
        HasValue = hasValue;
        Value = value;
    }

    public readonly bool HasValue;

    public readonly T Value;

    public IEnumerator<T> GetEnumerator()
    {
        if (HasValue) {
            yield return Value;
        }
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

[ProtoContract]
struct OptionSurrogate<T>
{
    public OptionSurrogate(Option<T> thing) {
        HasValue = thing.HasValue;
        Value = thing.Value;
    }
    
    [ProtoMember(1)]
    public readonly bool HasValue;
    
    [ProtoMember(2)]
    public readonly T Value;

    public static implicit operator Option<T>(OptionSurrogate<T> surrogate) => new Option<T>(surrogate.HasValue, surrogate.Value);
    public static implicit operator OptionSurrogate<T>(Option<T> value) => new OptionSurrogate<T>(value);
}

有两件事可以解决这个问题:

  1. struct Option&lt;T&gt; 更改为class Option&lt;T&gt;
  2. struct Option&lt;T&gt; 中删除IEnumerable&lt;T&gt;

但是,这些都不可能,因为我要序列化的 struct 位于第 3 方库中。

这是protobuf-net 中的错误还是有解决方法?

【问题讨论】:

标签: c# protobuf-net


【解决方案1】:

一种可行的解决方法是将对象包装在您自己的类中,您将能够控制哪些属性被序列化以及如何序列化。

所以我提取了protobuf 存储库,并逐步通过他们的代码来处理IEnumerable&lt;T&gt;Option&lt;T&gt;)的代理处理,他们的实现使得 heavy 假设任何IEnumerable&lt;T&gt; 都是@ 987654326@ 默认会导致此错误。 尽管您明确地向运行时提供了 Option&lt;T&gt; 不能使用 SetSurrogate 序列化的信息,但它们的实现在它打包到的项目之后才检查代理项更易于管理的Enumerable 类型。这里的问题是他们正在尝试打包:

public IEnumerator<T> GetEnumerator()
{
    if (HasValue) {
        yield return Value;
    }
}

这是不可能的(事实上,对于 IEnumerable 的这种实现,他们甚至没有“看到”/“看”。

话虽如此,我建议在反序列化之前和之后明确地转换对象,我提供了一个我在下面开始工作的示例。

static void Main()
{
    var typeModel = RuntimeTypeModel.Create();

    var value = new Option<int>(true, 5);

    using var writer = new FileInfo("test.txt").OpenWrite();

    typeModel.Serialize(writer , (OptionSurrogate<int>)value, typeModel);

    writer .Dispose();

    using var reader = new FileInfo("test.txt").OpenRead();

    Option<int> deserializedValue = (OptionSurrogate<int>)typeModel.Deserialize(reader, null, typeof(OptionSurrogate<int>));

}

编辑
添加了ICollection&lt;T&gt; 的简单实现以可能防止约束错误

编辑
这次添加了工作解决方法。

编者注
我不是protobuf 的专家,我对这个错误发生的方式和原因的看法是基于一些非常 简单的调试和逐步浏览他们的 github 源代码。这不应该被视为事实,因为我既没有写过protobuf,也没有使用protobuf 的源代码足够长的时间来正确地判断这个错误的原因。

【讨论】:

  • 谢谢,这适用于某些用例,但在我的用例中,我需要直接序列化 Option&lt;T&gt;
  • 您是否尝试过在包装器中实现ICollection?那may solve the TCollection constraint violation.
  • 好主意,我试过了,可惜还是不行
  • 我继续为您做了一些更深入的挖掘,查看更新后的帖子,希望对您有所帮助。
  • 很抱歉,不幸的是,它看起来像是对当前 protobuf 实现的限制,需要一些特殊处理。 =(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-18
  • 1970-01-01
相关资源
最近更新 更多