Создать прототип схемы во время выполнения?
Я использую protobuf.net в течение некоторого времени с его IExtensible
особенность (позволяет создавать потоки сообщений во время выполнения прото). К сожалению, кажется, что нет никакой функциональности для извлечения proto
схема из расширяемого класса. Мне нужна эта функциональность, чтобы немного проще получить protobuf.js для чтения потока сообщений.
Есть ли способ генерировать прототип схемы для расширяемого / динамического класса?
1 ответ
Это мое текущее решение; который работает, но не напрямую на расширяемый объект. Я не смог решить это. Так что, возможно, есть / будет лучше.
Чтобы создать тип (с атрибутами 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;
}