使用 Reflection.Emit 为给定属性列表生成接口实现
Generate interface implementation with Reflection.Emit for List of given properties
我正在使用 this question 中的代码从 属性
的列表中生成 class
public class Field
{
public string FieldName;
public Type FieldType;
}
public static class MyTypeBuilder
{
public static Type CompileResultType()
{
TypeBuilder tb = GetTypeBuilder();
ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
// NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type)
foreach (var field in yourListOfFields)
CreateProperty(tb, field.FieldName, field.FieldType);
Type objectType = tb.CreateType();
return objectType;
}
private static TypeBuilder GetTypeBuilder()
{
var typeSignature = "MyDynamicType";
var an = new AssemblyName(typeSignature);
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
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)
{
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);
}
}
我有接口到 get/set 它是避免使用反射和动态的属性
public interface IDynamicObject
{
T GetProperty<T>(string propertyName);
void SetProperty(string propertyName, object value);
}
任何人都可以帮我修改此代码以生成实现我的 IDynamicObject 接口的 class,以便它生成类似这样的内容(例如两个字符串属性“Str1”和“Str2”)吗?遗憾的是,我对 Reflection.Emit 还不太满意...
public class TestClass : IDynamicObject
{
public string Str1 { get; set; }
public string Str2 { get; set; }
public T GetProperty<T>(string propertyName)
{
switch (propertyName)
{
case nameof(Str1):
return CastObject<T>(Str1);
case nameof(Str2):
return CastObject<T>(Str2);
default: throw new ArgumentException();
}
}
public void SetProperty(string propertyName, object value)
{
switch (propertyName)
{
case nameof(Str1):
Str1 = (string)value;
break;
case nameof(Str2):
Str2 = (string)value;
break;
default: throw new ArgumentException();
}
}
public T CastObject<T>(object input)
{
return (T)input;
}
}
这是您可以使用的一种方法。这有点复杂,因为我们要用标签和 goto
逻辑
来实现它
在下面的代码中查看我的评论。
public static class MyTypeBuilder
{
public static void CompileAssembly(Field[] fields)
{
const string typeSignature = "MyDynamicType";
var assemblyName = new AssemblyName(typeSignature);
var assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule", $"{typeSignature}.dll");
CompileResultType(moduleBuilder, typeof(IDynamicObject), fields);
// save to review the compiled assembly
assemblyBuilder.Save($"{typeSignature}.dll");
}
private static Type CompileResultType(ModuleBuilder moduleBuilder, Type interfaceType, Field[] fields)
{
var typeBuilder = moduleBuilder.DefineType(moduleBuilder.Assembly.GetName().Name,
TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass |
TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
null, new[] { interfaceType });
// T CastObject<T>(object input)
var castObject = typeBuilder.DefineMethod("CastObject",
MethodAttributes.Public,
null, new[] { typeof(object) });
{
var castObjectOutputParameter = castObject.DefineGenericParameters("T")[0];
castObject.SetReturnType(castObjectOutputParameter);
castObject.DefineParameter(1, ParameterAttributes.None, "input");
var il = castObject.GetILGenerator();
il.DeclareLocal(castObjectOutputParameter);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ret);
}
// create properties in advance so we can reference them later
var properties = fields.ToDictionary(
key => key.FieldName,
value => CreateProperty(typeBuilder, value.FieldName, value.FieldType)
);
// T GetProperty<T>(string propertyName)
var getProperty = typeBuilder.DefineMethod("GetProperty",
MethodAttributes.Public | MethodAttributes.Final |
MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual,
null, new[] { typeof(string) });
{
// define generic parameter T
var outputParameter = getProperty.DefineGenericParameters("T")[0];
getProperty.SetReturnType(outputParameter);
// define name for the propertyName parameter
getProperty.DefineParameter(1, ParameterAttributes.None, "propertyName");
// reference to "operator ==" to compare a field name with the propertyName value
var stringEquals =
typeof(string).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public) ??
throw new InvalidOperationException();
var il = getProperty.GetILGenerator();
// declare local variables
il.DeclareLocal(typeof(string)); // loc_0 to store input for switch / case
il.DeclareLocal(outputParameter); // loc_1 to store result of the CastObject() call
// define general labels, we will mark their code locations later on
var returnLabel = il.DefineLabel(); // "return label"
var throwLabel = il.DefineLabel(); // "throw label" for throwing ArgumentException
// define "value labels" for each case body,
// use map to reference them later
var returnValueLabels = fields.ToDictionary(
key => key.FieldName,
value => il.DefineLabel()
);
// store propertyName in loc_0
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stloc_0);
foreach (var field in fields)
{
// check if propertyName == field.FieldName
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldstr, field.FieldName);
il.Emit(OpCodes.Call, stringEquals);
// if true, jump to the corresponding "return value" label
// we will mark a code location with it later (see next loop),
// right now we only need a reference
il.Emit(OpCodes.Brtrue, returnValueLabels[field.FieldName]);
}
// if we are here, that means the propertyName is unknown
// jump to the "throw label" location
il.Emit(OpCodes.Br, throwLabel);
foreach (var field in fields)
{
// mark the code with the corresponding "return value" label
il.MarkLabel(returnValueLabels[field.FieldName]);
// find a property we created before
// and pass its getter to the CastObject<T>() call
var property = properties[field.FieldName];
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, property.GetMethod);
il.Emit(OpCodes.Call, castObject);
// store result in loc_1
il.Emit(OpCodes.Stloc_1);
// jump to "return label"
il.Emit(OpCodes.Br, returnLabel);
}
// mark the following code that throws with "throw label"
il.MarkLabel(throwLabel);
// find ArgumentException(string) ctor
var argumentException =
typeof(ArgumentException).GetConstructor(new[] { typeof(string) }) ??
throw new InvalidOperationException();
// construct exception and throw
il.Emit(OpCodes.Ldstr, "propertyName");
il.Emit(OpCodes.Newobj, argumentException);
il.Emit(OpCodes.Throw);
// mark the following code with "return label"
il.MarkLabel(returnLabel);
// load value from loc_1
il.Emit(OpCodes.Ldloc_1);
// return
il.Emit(OpCodes.Ret);
}
// void SetProperty(string propertyName, object value)
// logic is very similar to GetProperty
var setProperty = typeBuilder.DefineMethod("SetProperty",
MethodAttributes.Public | MethodAttributes.Final |
MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual,
null, new[] { typeof(string), typeof(object) });
{
setProperty.DefineParameter(1, ParameterAttributes.None, "propertyName");
setProperty.DefineParameter(2, ParameterAttributes.None, "value");
var stringEquals =
typeof(string).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public) ??
throw new InvalidOperationException();
var il = setProperty.GetILGenerator();
il.DeclareLocal(typeof(string));
il.DeclareLocal(typeof(string));
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stloc_0);
var returnLabel = il.DefineLabel();
var throwLabel = il.DefineLabel();
var setValueLabels = fields.ToDictionary(
key => key.FieldName,
value => il.DefineLabel()
);
foreach (var field in fields)
{
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldstr, field.FieldName);
il.Emit(OpCodes.Call, stringEquals);
il.Emit(OpCodes.Brtrue, setValueLabels[field.FieldName]);
}
il.Emit(OpCodes.Br, throwLabel);
foreach (var field in fields)
{
var property = properties[field.FieldName];
il.MarkLabel(setValueLabels[field.FieldName]);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Castclass, field.FieldType);
il.Emit(OpCodes.Call, property.SetMethod);
il.Emit(OpCodes.Br, returnLabel);
}
il.MarkLabel(throwLabel);
var argumentException =
typeof(ArgumentException).GetConstructor(new[] { typeof(string) }) ??
throw new InvalidOperationException();
il.Emit(OpCodes.Ldstr, "propertyName");
il.Emit(OpCodes.Newobj, argumentException);
il.Emit(OpCodes.Throw);
il.MarkLabel(returnLabel);
il.Emit(OpCodes.Ret);
}
typeBuilder.DefineDefaultConstructor(
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName
);
return typeBuilder.CreateType();
}
private static PropertyBuilder CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
{
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);
return propertyBuilder;
}
}
这是它的工作原理
var fields = new[]
{
new Field { FieldName = "Str1", FieldType = typeof(string) },
new Field { FieldName = "Str2", FieldType = typeof(string) },
new Field { FieldName = "Str3", FieldType = typeof(string) }
};
MyTypeBuilder.CompileAssembly(fields);
...将生成以下内容:
public class MyDynamicType : IDynamicObject
{
// ... getters and setters
public T CastObject<T>(object input)
{
return (T)input;
}
public T GetProperty<T>(string propertyName)
{
switch (propertyName)
{
case "Str1":
return CastObject<T>(this.Str1);
case "Str2":
return CastObject<T>(this.Str2);
case "Str3":
return CastObject<T>(this.Str3);
default:
throw new ArgumentException("propertyName");
}
}
public void SetProperty(string propertyName, object value)
{
switch (propertyName)
{
case "Str1":
Str1 = (string)value;
break;
case "Str2":
Str2 = (string)value;
break;
case "Str3":
Str3 = (string)value;
break;
default:
throw new ArgumentException("propertyName");
}
}
}
我正在使用 this question 中的代码从 属性
的列表中生成 classpublic class Field
{
public string FieldName;
public Type FieldType;
}
public static class MyTypeBuilder
{
public static Type CompileResultType()
{
TypeBuilder tb = GetTypeBuilder();
ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
// NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type)
foreach (var field in yourListOfFields)
CreateProperty(tb, field.FieldName, field.FieldType);
Type objectType = tb.CreateType();
return objectType;
}
private static TypeBuilder GetTypeBuilder()
{
var typeSignature = "MyDynamicType";
var an = new AssemblyName(typeSignature);
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
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)
{
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);
}
}
我有接口到 get/set 它是避免使用反射和动态的属性
public interface IDynamicObject
{
T GetProperty<T>(string propertyName);
void SetProperty(string propertyName, object value);
}
任何人都可以帮我修改此代码以生成实现我的 IDynamicObject 接口的 class,以便它生成类似这样的内容(例如两个字符串属性“Str1”和“Str2”)吗?遗憾的是,我对 Reflection.Emit 还不太满意...
public class TestClass : IDynamicObject
{
public string Str1 { get; set; }
public string Str2 { get; set; }
public T GetProperty<T>(string propertyName)
{
switch (propertyName)
{
case nameof(Str1):
return CastObject<T>(Str1);
case nameof(Str2):
return CastObject<T>(Str2);
default: throw new ArgumentException();
}
}
public void SetProperty(string propertyName, object value)
{
switch (propertyName)
{
case nameof(Str1):
Str1 = (string)value;
break;
case nameof(Str2):
Str2 = (string)value;
break;
default: throw new ArgumentException();
}
}
public T CastObject<T>(object input)
{
return (T)input;
}
}
这是您可以使用的一种方法。这有点复杂,因为我们要用标签和 goto
逻辑
在下面的代码中查看我的评论。
public static class MyTypeBuilder
{
public static void CompileAssembly(Field[] fields)
{
const string typeSignature = "MyDynamicType";
var assemblyName = new AssemblyName(typeSignature);
var assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule", $"{typeSignature}.dll");
CompileResultType(moduleBuilder, typeof(IDynamicObject), fields);
// save to review the compiled assembly
assemblyBuilder.Save($"{typeSignature}.dll");
}
private static Type CompileResultType(ModuleBuilder moduleBuilder, Type interfaceType, Field[] fields)
{
var typeBuilder = moduleBuilder.DefineType(moduleBuilder.Assembly.GetName().Name,
TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass |
TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
null, new[] { interfaceType });
// T CastObject<T>(object input)
var castObject = typeBuilder.DefineMethod("CastObject",
MethodAttributes.Public,
null, new[] { typeof(object) });
{
var castObjectOutputParameter = castObject.DefineGenericParameters("T")[0];
castObject.SetReturnType(castObjectOutputParameter);
castObject.DefineParameter(1, ParameterAttributes.None, "input");
var il = castObject.GetILGenerator();
il.DeclareLocal(castObjectOutputParameter);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ret);
}
// create properties in advance so we can reference them later
var properties = fields.ToDictionary(
key => key.FieldName,
value => CreateProperty(typeBuilder, value.FieldName, value.FieldType)
);
// T GetProperty<T>(string propertyName)
var getProperty = typeBuilder.DefineMethod("GetProperty",
MethodAttributes.Public | MethodAttributes.Final |
MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual,
null, new[] { typeof(string) });
{
// define generic parameter T
var outputParameter = getProperty.DefineGenericParameters("T")[0];
getProperty.SetReturnType(outputParameter);
// define name for the propertyName parameter
getProperty.DefineParameter(1, ParameterAttributes.None, "propertyName");
// reference to "operator ==" to compare a field name with the propertyName value
var stringEquals =
typeof(string).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public) ??
throw new InvalidOperationException();
var il = getProperty.GetILGenerator();
// declare local variables
il.DeclareLocal(typeof(string)); // loc_0 to store input for switch / case
il.DeclareLocal(outputParameter); // loc_1 to store result of the CastObject() call
// define general labels, we will mark their code locations later on
var returnLabel = il.DefineLabel(); // "return label"
var throwLabel = il.DefineLabel(); // "throw label" for throwing ArgumentException
// define "value labels" for each case body,
// use map to reference them later
var returnValueLabels = fields.ToDictionary(
key => key.FieldName,
value => il.DefineLabel()
);
// store propertyName in loc_0
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stloc_0);
foreach (var field in fields)
{
// check if propertyName == field.FieldName
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldstr, field.FieldName);
il.Emit(OpCodes.Call, stringEquals);
// if true, jump to the corresponding "return value" label
// we will mark a code location with it later (see next loop),
// right now we only need a reference
il.Emit(OpCodes.Brtrue, returnValueLabels[field.FieldName]);
}
// if we are here, that means the propertyName is unknown
// jump to the "throw label" location
il.Emit(OpCodes.Br, throwLabel);
foreach (var field in fields)
{
// mark the code with the corresponding "return value" label
il.MarkLabel(returnValueLabels[field.FieldName]);
// find a property we created before
// and pass its getter to the CastObject<T>() call
var property = properties[field.FieldName];
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, property.GetMethod);
il.Emit(OpCodes.Call, castObject);
// store result in loc_1
il.Emit(OpCodes.Stloc_1);
// jump to "return label"
il.Emit(OpCodes.Br, returnLabel);
}
// mark the following code that throws with "throw label"
il.MarkLabel(throwLabel);
// find ArgumentException(string) ctor
var argumentException =
typeof(ArgumentException).GetConstructor(new[] { typeof(string) }) ??
throw new InvalidOperationException();
// construct exception and throw
il.Emit(OpCodes.Ldstr, "propertyName");
il.Emit(OpCodes.Newobj, argumentException);
il.Emit(OpCodes.Throw);
// mark the following code with "return label"
il.MarkLabel(returnLabel);
// load value from loc_1
il.Emit(OpCodes.Ldloc_1);
// return
il.Emit(OpCodes.Ret);
}
// void SetProperty(string propertyName, object value)
// logic is very similar to GetProperty
var setProperty = typeBuilder.DefineMethod("SetProperty",
MethodAttributes.Public | MethodAttributes.Final |
MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual,
null, new[] { typeof(string), typeof(object) });
{
setProperty.DefineParameter(1, ParameterAttributes.None, "propertyName");
setProperty.DefineParameter(2, ParameterAttributes.None, "value");
var stringEquals =
typeof(string).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public) ??
throw new InvalidOperationException();
var il = setProperty.GetILGenerator();
il.DeclareLocal(typeof(string));
il.DeclareLocal(typeof(string));
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stloc_0);
var returnLabel = il.DefineLabel();
var throwLabel = il.DefineLabel();
var setValueLabels = fields.ToDictionary(
key => key.FieldName,
value => il.DefineLabel()
);
foreach (var field in fields)
{
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldstr, field.FieldName);
il.Emit(OpCodes.Call, stringEquals);
il.Emit(OpCodes.Brtrue, setValueLabels[field.FieldName]);
}
il.Emit(OpCodes.Br, throwLabel);
foreach (var field in fields)
{
var property = properties[field.FieldName];
il.MarkLabel(setValueLabels[field.FieldName]);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Castclass, field.FieldType);
il.Emit(OpCodes.Call, property.SetMethod);
il.Emit(OpCodes.Br, returnLabel);
}
il.MarkLabel(throwLabel);
var argumentException =
typeof(ArgumentException).GetConstructor(new[] { typeof(string) }) ??
throw new InvalidOperationException();
il.Emit(OpCodes.Ldstr, "propertyName");
il.Emit(OpCodes.Newobj, argumentException);
il.Emit(OpCodes.Throw);
il.MarkLabel(returnLabel);
il.Emit(OpCodes.Ret);
}
typeBuilder.DefineDefaultConstructor(
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName
);
return typeBuilder.CreateType();
}
private static PropertyBuilder CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
{
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);
return propertyBuilder;
}
}
这是它的工作原理
var fields = new[]
{
new Field { FieldName = "Str1", FieldType = typeof(string) },
new Field { FieldName = "Str2", FieldType = typeof(string) },
new Field { FieldName = "Str3", FieldType = typeof(string) }
};
MyTypeBuilder.CompileAssembly(fields);
...将生成以下内容:
public class MyDynamicType : IDynamicObject
{
// ... getters and setters
public T CastObject<T>(object input)
{
return (T)input;
}
public T GetProperty<T>(string propertyName)
{
switch (propertyName)
{
case "Str1":
return CastObject<T>(this.Str1);
case "Str2":
return CastObject<T>(this.Str2);
case "Str3":
return CastObject<T>(this.Str3);
default:
throw new ArgumentException("propertyName");
}
}
public void SetProperty(string propertyName, object value)
{
switch (propertyName)
{
case "Str1":
Str1 = (string)value;
break;
case "Str2":
Str2 = (string)value;
break;
case "Str3":
Str3 = (string)value;
break;
default:
throw new ArgumentException("propertyName");
}
}
}