0

我一直在使用 protobuf.net 的IExtensible功能(允许我创建运行时原型消息流)。不幸的是,似乎没有任何功能可以proto从可扩展类中提取模式。我需要这个功能,以便更容易让 protobuf.js 读取消息流。

有没有办法为可扩展/动态类生成原型模式?

4

1 回答 1

0

这是我目前的解决方案;这确实有效,但不能直接在 Extensible 对象上。我无法解决这个问题。所以也许有/将会有更好的方法。

创建类型(使用 ProtoMember 属性)

using ProtoField = System.Tuple<string, Type>; 

public static class ProtoTypeBuilder
{
    public static Type CompileResultType(string typeName, IEnumerable<ProtoField>> fields)
    {
        TypeBuilder tb = GetTypeBuilder(typeName);

        ConstructorBuilder constructor =
            tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName |
                                        MethodAttributes.RTSpecialName);

        ConstructorInfo contractInfoCon =
            typeof (ProtoBuf.ProtoContractAttribute).GetConstructor(Array.Empty<Type>());
        CustomAttributeBuilder cab = new CustomAttributeBuilder(contractInfoCon, Array.Empty<object>());

        tb.SetCustomAttribute(cab);

        foreach (var field in fields.Select((x, i) => new {Item = x, Index = i + 1}))
            CreateProperty(tb, field.Item.Item1, field.Item.Item2, field.Index);

        Type objectType = tb.CreateType();
        return objectType;
    }

    private static TypeBuilder GetTypeBuilder(string typeName)
    {
        var typeSignature = "DynamicProtoTypes";
        var an = new AssemblyName(typeSignature);
        AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an,
            AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
        TypeBuilder tb = moduleBuilder.DefineType(typeName
            , TypeAttributes.Public |
              TypeAttributes.Class |
              TypeAttributes.AutoClass |
              TypeAttributes.AnsiClass |
              TypeAttributes.BeforeFieldInit |
              TypeAttributes.AutoLayout
            , null);
        return tb;
    }

    private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType, int protoTag)
    {
        FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

        PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault,
            propertyType, null);
        MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName,
            MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType,
            Type.EmptyTypes);
        ILGenerator getIl = getPropMthdBldr.GetILGenerator();

        getIl.Emit(OpCodes.Ldarg_0);
        getIl.Emit(OpCodes.Ldfld, fieldBuilder);
        getIl.Emit(OpCodes.Ret);

        MethodBuilder setPropMthdBldr =
            tb.DefineMethod("set_" + propertyName,
                MethodAttributes.Public |
                MethodAttributes.SpecialName |
                MethodAttributes.HideBySig,
                null, new[] {propertyType});

        ILGenerator setIl = setPropMthdBldr.GetILGenerator();
        Label modifyProperty = setIl.DefineLabel();
        Label exitSet = setIl.DefineLabel();

        setIl.MarkLabel(modifyProperty);
        setIl.Emit(OpCodes.Ldarg_0);
        setIl.Emit(OpCodes.Ldarg_1);
        setIl.Emit(OpCodes.Stfld, fieldBuilder);

        setIl.Emit(OpCodes.Nop);
        setIl.MarkLabel(exitSet);
        setIl.Emit(OpCodes.Ret);

        propertyBuilder.SetGetMethod(getPropMthdBldr);
        propertyBuilder.SetSetMethod(setPropMthdBldr);

        ConstructorInfo contractInfoCon = typeof (ProtoBuf.ProtoMemberAttribute).GetConstructor(new[] {typeof (int)});
        CustomAttributeBuilder cab = new CustomAttributeBuilder(contractInfoCon, new object[] {protoTag});
        propertyBuilder.SetCustomAttribute(cab);
    }
}

示例用法:

internal class Program
{
    static void Main(string[] args)
    {
        var foo = ProtoTypeBuilder.CompileResultType("Foo", new[]
        {
            new ProtoField ("A", typeof (int)),
            new ProtoField ("B", typeof (int)),
        });

        var record = ProtoTypeBuilder.CompileResultType("Record", new[]
        {
            new ProtoField ("Integer", typeof (int)),
            new ProtoField ("Number", typeof (double)),
            new ProtoField ("String", typeof (string)),
            new ProtoField ("MyFoo", foo),
        });

        var proto = ProtoBuf.Meta.RuntimeTypeModel.Default.GetSchema(record);

        Console.WriteLine(proto);
    }
} 

输出:

message Foo {
   optional int32 A = 1 [default = 0];
   optional int32 B = 2 [default = 0];
}
message Record {
   optional int32 Integer = 1 [default = 0];
   optional double Number = 2 [default = 0];
   optional string String = 3;
   optional Foo MyFoo = 4;
}
于 2015-11-17T11:30:42.057 回答