protobuf-net - 从.proto生成的类 - 重复字段是否应该是只读的,没有setter?

26

我对此完全感到困惑。我已经四处查找,但似乎找不到直接的答案。我的项目是Java编写的,其中使用了.proto文件创建了一些消息。

有一个重复的Info字段,这是我们创建的一种类型。当我使用protogen生成C#类时,这个字段会变成只读,并且没有setter。

如果没有这个参数,我就无法完整地构建消息。所以我的问题是:重复字段是否应该像这样生成,而我应该通过其他方式访问这个只读列表?还是生成器中存在错误?

生成的代码:

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; }
}

Proto文件部分:

repeated StringMapEntry factoryProperty = 2;

我可能只是漏看了一个非常显而易见的东西。感谢任何帮助!

2个回答

44

这个列表不是只读的...你可以改变 它给你的列表:

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

子集合通常是只读的,但这并不意味着您无法更改其内容。


列表被创建为私有只读,我想我只是没有看到如何操作它。不过我稍后会再看一下,我们采用了不同的方法来解决这个问题,我忘记了这个。可能只是我太糊涂了。谢谢你的帮助!如果这就是问题所在,我会回来标记它已解决。 - Mimerr
@user 应该有一个公共属性。难道没有吗? - Marc Gravell
我会在主帖中更新代码,但我还没有再次检查它的机会。 - Mimerr
_factoryProperty 上的 private readonly 只是意味着即使该类在构建/初始化后也不允许更改该变量。也就是说,不允许将变量 _factoryProperty 更改为除该列表以外的任何内容。但是列表本身并不是只读的。 - Jesse Chisholm
@JesseChisholm 都是真的,但这感觉应该是对问题的评论?我甚至没有提到 _factoryProperty... - Marc Gravell
@MarcGravell - 哎呀!你说得对。我点击了错误的“添加评论”链接。唉。 - Jesse Chisholm

1
这对我们来说是一个新问题,因为我们更新了proto-net可执行文件和相关文件。这是我们以前没有遇到过的新行为。
在中进行了一些挖掘后,我们找到了“重复”字段的定义:
<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 将被正确生成。
因此,它变成了: private ...snip...
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,但是它没有起作用,你可能仍然希望启用该选项。

1
没有 set 只意味着你不能执行 msg.field = null; 或者 msg.field = otherList;。但是它并不会阻止你执行 msg.field.Clear(); 或者 msg.field.AddRange(otherList); - Jesse Chisholm
@JesseChisholm 使用自动映射工具真是一件痛苦的事情。 - SHM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接