【问题标题】:protobuf-net - generated class from .proto - Is repeated field supposed to be Read Only with no setter?protobuf-net - 从 .proto 生成的类 - 重复字段是否应该是只读的,没有设置器?
【发布时间】:2013-05-13 03:31:06
【问题描述】:

我对此感到非常困惑。我环顾四周,似乎找不到直接的答案。我有一个 .proto 文件,我的项目一直是 java,用它来创建一些消息。

有一个重复的信息字段。这是我们创建的一种类型。当我使用 protogen 生成 C# 类时,该字段显示为只读且没有设置器。

如果没有此参数,我无法完全构建消息。所以我的问题是。是否应该像这样生成重复字段并且我应该以其他方式访问这个只读列表?或者这是生成器中的错误?

生成的代码:

private readonly global::System.Collections.Generic.List<StringMapEntry> _factoryProperty = new global::System.Collections.Generic.List<StringMapEntry>();
[global::ProtoBuf.ProtoMember(2, Name=@"factoryProperty", DataFormat = global::ProtoBuf.DataFormat.Default)]
public global::System.Collections.Generic.List<StringMapEntry> factoryProperty
{
  get { return _factoryProperty; }
}

原始文件部分:

repeated StringMapEntry factoryProperty = 2;

我可能只是错过了一些非常明显的东西。感谢您的帮助!

【问题讨论】:

    标签: c# protobuf-net setter readonly protogen


    【解决方案1】:

    该列表不是只读的...您只需修改 它为您提供的列表

    var order = new Order();
    order.Lines.Add( new OrderLine {...} );
    

    实际上,子集合只能获取是很常见的。这并不意味着您不能更改内容。

    【讨论】:

    • 列表是作为私有只读创建的,我想我只是不知道如何处理它。不过,我稍后将不得不查看它,我们使用了不同的方法来解决问题,我忘记了这一点。可能只是我瞎了。不过感谢您的帮助!如果是这样,我会回来并标记为已回答。
    • @user 应该有一个公共属性。没有吗?
    • 我将用代码更新主帖,但我仍然没有机会再次检查它。
    • private readonly 上的_factoryProperty 仅意味着即使该类也不允许在其构造/初始化后更改该变量。也就是说,不允许将变量_factoryProperty 更改为该列表以外的任何内容。但列表本身不是只读的。
    • @JesseChisholm 都是真的,但这感觉应该是对这个问题的评论?我什至没有提到_factoryProperty...
    【解决方案2】:

    在更新我们的 proto-net 可执行文件和相关文件后,这对我们来说也是一个新问题。这是我们以前从未经历过的新行为。

    csharp.xslt 中稍作挖掘后,我们找到了“重复”字段的定义:

    <xsl:template match="FieldDescriptorProto[label='LABEL_REPEATED']">
        <xsl:variable name="type"><xsl:apply-templates select="." mode="type"/></xsl:variable>
        <xsl:variable name="format"><xsl:apply-templates select="." mode="format"/></xsl:variable>
        <xsl:variable name="field"><xsl:apply-templates select="." mode="field"/></xsl:variable>
        private <xsl:if test="not($optionXml)">readonly</xsl:if> global::System.Collections.Generic.List&lt;<xsl:value-of select="$type" />&gt; <xsl:value-of select="$field"/> = new global::System.Collections.Generic.List&lt;<xsl:value-of select="$type"/>&gt;();
        [<xsl:apply-templates select="." mode="checkDeprecated"/>global::ProtoBuf.ProtoMember(<xsl:value-of select="number"/>, Name=@"<xsl:value-of select="name"/>", DataFormat = global::ProtoBuf.DataFormat.<xsl:value-of select="$format"/><xsl:if test="options/packed='true'">, Options = global::ProtoBuf.MemberSerializationOptions.Packed</xsl:if>)]<!--
        --><xsl:if test="$optionDataContract">
        [global::System.Runtime.Serialization.DataMember(Name=@"<xsl:value-of select="name"/>", Order = <xsl:value-of select="number"/>, IsRequired = false)]
        </xsl:if><xsl:if test="$optionXml">
        [global::System.Xml.Serialization.XmlElement(@"<xsl:value-of select="name"/>", Order = <xsl:value-of select="number"/>)]
        </xsl:if>
        public global::System.Collections.Generic.List&lt;<xsl:value-of select="$type" />&gt; <xsl:call-template name="pascal"/>
        {
          get { return <xsl:value-of select="$field"/>; }<!--
          --><xsl:if test="$optionXml">
          set { <xsl:value-of select="$field"/> = value; }</xsl:if>
        }
      </xsl:template>
    

    我已经提取了私有字段和 setter 的特定部分:

    private <xsl:if test="not($optionXml)">readonly</xsl:if> ...snip...
    
    public ...snip...
    {
      ...snip... 
      <!----><xsl:if test="$optionXml">
      set { <xsl:value-of select="$field"/> = value; }
      </xsl:if>
    }
    

    请注意上面 $optionXml 的可疑情况。如果您只是删除它们,则该字段不再是只读的,并且 setter 会正确生成。

    于是就变成了: 私人的...剪辑...

    public ...snip...
    {
      ...snip... 
      set { <xsl:value-of select="$field"/> = value; }
    }
    

    完整的“固定”模板:

      <xsl:template match="FieldDescriptorProto[label='LABEL_REPEATED']">
        <xsl:variable name="type"><xsl:apply-templates select="." mode="type"/></xsl:variable>
        <xsl:variable name="format"><xsl:apply-templates select="." mode="format"/></xsl:variable>
        <xsl:variable name="field"><xsl:apply-templates select="." mode="field"/></xsl:variable>
        private global::System.Collections.Generic.List&lt;<xsl:value-of select="$type" />&gt; <xsl:value-of select="$field"/> = new global::System.Collections.Generic.List&lt;<xsl:value-of select="$type"/>&gt;();
        [<xsl:apply-templates select="." mode="checkDeprecated"/>global::ProtoBuf.ProtoMember(<xsl:value-of select="number"/>, Name=@"<xsl:value-of select="name"/>", DataFormat = global::ProtoBuf.DataFormat.<xsl:value-of select="$format"/><xsl:if test="options/packed='true'">, Options = global::ProtoBuf.MemberSerializationOptions.Packed</xsl:if>)]<!--
        --><xsl:if test="$optionDataContract">
        [global::System.Runtime.Serialization.DataMember(Name=@"<xsl:value-of select="name"/>", Order = <xsl:value-of select="number"/>, IsRequired = false)]
        </xsl:if><xsl:if test="$optionXml">
        [global::System.Xml.Serialization.XmlElement(@"<xsl:value-of select="name"/>", Order = <xsl:value-of select="number"/>)]
        </xsl:if>
        public global::System.Collections.Generic.List&lt;<xsl:value-of select="$type" />&gt; <xsl:call-template name="pascal"/>
        {
          get { return <xsl:value-of select="$field"/>; }
          set { <xsl:value-of select="$field"/> = value; }
        }
      </xsl:template>
    

    我尝试将 optionXml 设置为 false,但它不起作用,您可能仍希望启用该选项。

    【讨论】:

    • 没有set 只是意味着你不能做msg.field = null;msg.field = otherList; 它并不妨碍你做msg.field.Clear();msg.field.AddRange(otherList);
    • @JesseChisholm 使用自动映射工具很痛苦。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-11
    • 1970-01-01
    • 2020-01-01
    • 1970-01-01
    相关资源
    最近更新 更多