我正在尝试在运行时动态创建一个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
类的max
或ProcessData
属性的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);
答案 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 我会在这里放置我用于生成类Hist
和ProcessData
的代码,可能会对您有所帮助,如果我的解释不充分。
此属性创建助手的主要逻辑:
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);