C# 使用FieldDescriptor迭代未知的重复protobuf字段

C# 使用FieldDescriptor迭代未知的重复protobuf字段,c#,iterator,protocol-buffers,C#,Iterator,Protocol Buffers,我试图编写一个通用的方法来迭代protobuf消息,基本上创建一个对象树 到目前为止我有 IMessage message = new Message() as IMessage; foreach (FieldDescriptor field in message.Descriptor.Fields.InFieldNumberOrder()) { if (field.FieldType == Google.Protobuf.Reflecti

我试图编写一个通用的方法来迭代protobuf消息,基本上创建一个对象树

到目前为止我有

IMessage message = new Message() as IMessage;
foreach (FieldDescriptor field in message.Descriptor.Fields.InFieldNumberOrder())
            {
                if (field.FieldType == Google.Protobuf.Reflection.FieldType.Message)
                {
                    if (field.IsRepeated)
                    {
                        // How to do this?
                    }
                    else
                    {
                        Children.Add(new MessageNode(field, (IMessage)field.Accessor.GetValue(message)));
                    }
                }
                else
                {
                    Children.Add(new FieldNode(field, message));
                }
           }
除了重复的字段外,工作正常(地图字段可能会出现同样的问题)

如果对重复字段执行
field.Accessor.GetValue(message)
,则会得到一个
对象[RepeatedField]
,其中
T
是一些嵌套消息。但是,我无法迭代类型为
object
的内容。 我尝试将对象强制转换为
RepeatedField
,但这只返回0


是否有任何方法可以在不知道内部类型的情况下迭代重复的字段?我只需要一个指向内部对象的
IMessage
指针,从那里我可以使用
IMessage
中的
描述符
,我不知道在过去两天里我有多瞎试图解决这个问题

Accessor
GetValue
文档中,它说

对于重复的值,这将是IList实现。对于映射值,这将是一个IDictionary实现

是的,
IList d=field.Accessor.GetValue(message)作为IList
执行此操作并提供重复字段的可编辑集合。

如中所述,根据
字段。Accessor.GetValue(protobufMessage)
将为重复字段返回一个
IList
,为映射字段返回一个
IDictionary

下面是我编写的一些代码,它使用字段描述符递归地遍历protobuf消息。它可能是执行其他操作而不是打印的有用起点

    public static void PrintProtobufMessage(IMessage protobufMessage)
    {
        if (protobufMessage == null)
        {
            return;
        }
        foreach (var field in protobufMessage.Descriptor.Fields.InFieldNumberOrder())
        {
            if (field.IsMap)
            {
                // for map fields, the field type is always a MapField, inside of which are contained a keyField and valueField
                //var keyField = field.MessageType.Fields.InFieldNumberOrder()[0];
                var valueField = field.MessageType.Fields.InFieldNumberOrder()[1];
                IDictionary mapFields = field.Accessor.GetValue(protobufMessage) as IDictionary;
                if (mapFields != null)
                {
                    foreach (DictionaryEntry mapField in mapFields)
                    {
                        Console.WriteLine("Iterating Map, Field {0} ({1}): Key {2}:", field.FieldNumber, field.Name, mapField.Key);
                        if (valueField.FieldType == Google.Protobuf.Reflection.FieldType.Message)
                        {
                            PrintProtobufMessage(mapField.Value as IMessage);
                        }
                        else
                        {
                            PrintProtobufField(valueField, mapField.Value);
                        }
                    }
                }
            }
            else if (field.IsRepeated)
            {
                IList repeatedFieldValues = field.Accessor.GetValue(protobufMessage) as IList;
                if (repeatedFieldValues != null)
                {
                    foreach (var repeatedFieldValue in repeatedFieldValues)
                    {
                        // for repeated fields, the field type represents each element in the list, handle them accordingly
                        if (field.FieldType == Google.Protobuf.Reflection.FieldType.Message)
                        {
                            PrintProtobufMessage(repeatedFieldValue as IMessage);
                        }
                        else
                        {
                            PrintProtobufField(field, repeatedFieldValue);
                        }
                    }
                }
            }
            else
            {
                if (field.FieldType == Google.Protobuf.Reflection.FieldType.Message)
                {
                    var fieldValue = field.Accessor.GetValue(protobufMessage);
                    PrintProtobufMessage(fieldValue as IMessage);
                }
                else
                {
                    PrintProtobufField(field, protobufMessage);
                }
            }
        }
    }

    public static void PrintProtobufField(FieldDescriptor field, IMessage protobufMessage)
    {                   
        var fieldValue = field.Accessor.GetValue(protobufMessage);
        PrintProtobufField(field, fieldValue);
    }

    public static void PrintProtobufField(FieldDescriptor field, object fieldValue)
    {
        if (fieldValue != null)
        {
            Console.WriteLine(
                "Field {0} ({1}): {2}",
                field.FieldNumber,
                field.Name,
                fieldValue
            );
        }
    }