在运行时在C#中创建动态类型

时间:2017-01-04 12:33:14

标签: c# dynamic-assemblies typebuilder

我正在尝试在运行时动态创建一个c#类。

using System;

class Hist
{
  private int? _min;
  private  int? _max;

  public int? min
  {
      get{return _min;}
      set {_min = value;}
  }

  public int? max
  {
      get{return _max;}
      set {_max = value;}
  }
}

public class ProcessData
{
  private string _id;
  private string _source;
  private int? _currentValue;
  private Hist _hist;

  public Hist hist
  {
      get { return _hist; }
      set{ _hist = value; }
  }

  public string id 
  {
      get {return _id;}
      set { _id = value; }
  }

  public string source 
  {
      get {return _source;}
      set { _source = value; }
  }

  public int? currentValue 
  {
       get {return _currentValue;}
       set { _currentValue = value; }
  }

  public int? min
  {
      get { return (hist != null) ? hist.min : null; }        
  }
  public int? max
  {
      get { return (hist != null) ? hist.max : null; }        
  }
}

但我无法专门做到这一点。

return (hist != null) ? hist.max : null;

我只需要min类的maxProcessData属性的get方法。

上述任务的代码:

var method = parentType.GetMethod("get_" + propertyName);
getPropMthdBldr = tb.DefineMethod("get_" + propertyName, 
      MethodAttributes.Public | 
      MethodAttributes.SpecialName | 
      MethodAttributes.HideBySig, 
    propertyType, Type.EmptyTypes);
getIl = getPropMthdBldr.GetILGenerator();
var moveTo = getIl.DefineLabel();
getIl.Emit(OpCodes.Ldarg_0);
getIl.EmitCall(OpCodes.Call, parentGetMethod, Type.EmptyTypes);
getIl.Emit(OpCodes.Brtrue_S, moveTo);
getIl.Emit(OpCodes.Ldloca_S, 0);
getIl.Emit(OpCodes.Initobj, typeof(int?));
getIl.Emit(OpCodes.Ldloc_0);
getIl.Emit(OpCodes.Ret);
getIl.MarkLabel(moveTo);
getIl.Emit(OpCodes.Ldarg_0);
getIl.EmitCall(OpCodes.Call, parentGetMethod,Type.EmptyTypes);
getIl.EmitCall(OpCodes.Callvirt, method,Type.EmptyTypes);
getIl.Emit(OpCodes.Ret); 

1 个答案:

答案 0 :(得分:0)

问题是您正在尝试使用未声明的局部变量:

getIl.Emit(OpCodes.Ldloca_S, 0);           // load address of local variable with index 0 on stack 
getIl.Emit(OpCodes.Initobj, typeof(int?)); // initialize local variable
getIl.Emit(OpCodes.Ldloc_0);               // load value of local variable with index 0 on stack

您可以像这样定义所需的局部变量:

var local = getIl.DeclareLocal(typeof(int?));

您的代码有效,但为了提高可读性,我建议您使用变量local代替索引。可以这样做:

getIl.Emit(OpCodes.Ldloca_S, local);       // load address of local variable on stack
getIl.Emit(OpCodes.Initobj, typeof(int?)); // initialize local variable
getIl.Emit(OpCodes.Ldloc, local);          // load value of local variable on stack

PS 我会在这里放置我用于生成类HistProcessData的代码,可能会对您有所帮助,如果我的解释不充分。

此属性创建助手的主要逻辑:

public static class TypeBuilderExtensions
{
    public static PropertyBuilder CreateProperty<T>(this TypeBuilder builder, string name) => CreateProperty(builder, typeof(T), name);

    public static PropertyBuilder CreateProperty(this TypeBuilder builder, Type propertyType,  string name)
    {
        var field = builder.DefineField($"_{name}", propertyType, FieldAttributes.Private);
        var getMethodBuilder = builder.DefineMethod($"get_{name}", MethodAttributes.Public, propertyType, Type.EmptyTypes);
        var getGenerator = getMethodBuilder.GetILGenerator();
        getGenerator.Emit(OpCodes.Ldarg_0);
        getGenerator.Emit(OpCodes.Ldfld, field);
        getGenerator.Emit(OpCodes.Ret);

        var setMethodBuilder = builder.DefineMethod($"set_{name}", MethodAttributes.Public, typeof(void), new[] { propertyType });
        var setGenerator = setMethodBuilder.GetILGenerator();
        setGenerator.Emit(OpCodes.Ldarg_0);
        setGenerator.Emit(OpCodes.Ldarg_1);
        setGenerator.Emit(OpCodes.Stfld, field);
        setGenerator.Emit(OpCodes.Ret);

        var propertyBuilder = builder.DefineProperty(name, PropertyAttributes.None, propertyType, Type.EmptyTypes);
        propertyBuilder.SetGetMethod(getMethodBuilder);
        propertyBuilder.SetSetMethod(setMethodBuilder);
        return propertyBuilder;
    }

    public static PropertyBuilder CreateCalculatedProperty<T>(this TypeBuilder builder, string name, MethodInfo getObject, MethodInfo getObjectProperty) => CreateCalculatedProperty(builder, typeof(T), name, getObject, getObjectProperty);

    public static PropertyBuilder CreateCalculatedProperty(this TypeBuilder builder, Type propertyType, string name, MethodInfo getObject, MethodInfo getObjectProperty)
    {
        var getMethodBuilder = builder.DefineMethod($"get_{name}", MethodAttributes.Public, propertyType, Type.EmptyTypes);
        var getGenerator = getMethodBuilder.GetILGenerator();
        var label = getGenerator.DefineLabel();
        var local = getGenerator.DeclareLocal(propertyType);
        getGenerator.Emit(OpCodes.Ldarg_0);
        getGenerator.Emit(OpCodes.Callvirt, getObject);
        getGenerator.Emit(OpCodes.Brtrue, label);
        getGenerator.Emit(OpCodes.Ldloca_S, local);
        getGenerator.Emit(OpCodes.Initobj, propertyType);
        getGenerator.Emit(OpCodes.Ldloc, local);
        getGenerator.Emit(OpCodes.Ret);
        getGenerator.MarkLabel(label);
        getGenerator.Emit(OpCodes.Ldarg_0);
        getGenerator.Emit(OpCodes.Callvirt, getObject);
        getGenerator.Emit(OpCodes.Callvirt, getObjectProperty);
        getGenerator.Emit(OpCodes.Ret);

        var propertyBuilder = builder.DefineProperty(name, PropertyAttributes.None, propertyType, Type.EmptyTypes);
        propertyBuilder.SetGetMethod(getMethodBuilder);
        return propertyBuilder;
    }
}

这是类创建本身:

var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("TestModule");
var histBuilder = moduleBuilder.DefineType("Hist");
var minProperty = histBuilder.CreateProperty<int?>("min");
var maxProperty = histBuilder.CreateProperty<int?>("max");

var processDataBuilder = moduleBuilder.DefineType("ProcessData");
var histProperty = processDataBuilder.CreateProperty(histBuilder, "hist");
processDataBuilder.CreateProperty<string>("id");
processDataBuilder.CreateProperty<string>("source");
processDataBuilder.CreateProperty<int?>("currentValue");

processDataBuilder.CreateCalculatedProperty<int?>("min", histProperty.GetMethod, minProperty.GetMethod);
processDataBuilder.CreateCalculatedProperty<int?>("max", histProperty.GetMethod, maxProperty.GetMethod);

最后对创建的类进行原始验证:

void ValidateProperty(object instance, string name, object value, bool setValue = true)
{
    var type = instance.GetType();
    var property = type.GetProperty(name);
    if (setValue) property.SetValue(instance, value);
    var result = property.GetValue(instance);

    var equals = property.PropertyType.IsValueType && value != null ? value.Equals(result) : value == result;
    if (!equals)
        throw new InvalidDataException("Property not valid");
}

var histType = histBuilder.CreateType();
var histInstance = Activator.CreateInstance(histType);
ValidateProperty(histInstance, "min", 12);
ValidateProperty(histInstance, "max", 21);

var processDataType = processDataBuilder.CreateType();
var processDataInstance = Activator.CreateInstance(processDataType);
ValidateProperty(processDataInstance, "hist", histInstance);
ValidateProperty(processDataInstance, "id", "Test!");
ValidateProperty(processDataInstance, "source", "Source#");
ValidateProperty(processDataInstance, "currentValue", 126);

ValidateProperty(processDataInstance, "min", 12, false);
ValidateProperty(processDataInstance, "max", 21, false);

ValidateProperty(processDataInstance, "hist", null);
ValidateProperty(processDataInstance, "min", null, false);
ValidateProperty(processDataInstance, "max", null, false);