0

私は protobuf.net をその機能でしばらく使用してきましたIExtensible(ランタイム プロト メッセージ ストリームを作成できます)。proto残念ながら、 Extensible クラスからスキーマを抽出する機能はないようです。protobuf.js でメッセージ ストリームを読み取るのが少し簡単になるように、この機能が必要です。

Extensible/dynamic クラスの proto スキーマを生成する方法はありますか?

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 に答える