2015-11-17 110 views
0

我一直在使用protobuf.net一段時間的IExtensible功能(允許我創建運行時原型消息流)。不幸的是,似乎沒有任何功能從Extensible類中提取proto模式。我需要這個功能,以便讓protobuf.js更容易讀取消息流。在運行時創建原型模式?

有沒有辦法爲可擴展/動態類生成proto模式?

回答

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