Access 自动创建匿名类型字段
Access automatically created anonymus type fields
让我们成像,我们发出一个 class 代表圆圈。我们定义了一个双 属性 表示它的半径,具有相关的支持字段和 get/set 访问器。然后我们准备一个计算区域的逻辑,以匿名方法的形式,使用新创建的PropertyBuilder。
var assemblyName = new AssemblyName();
assemblyName.Name = "SampleAssembly";
var domain = Thread.GetDomain();
var assembly = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
var module = assembly.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll", true);
var baseType = typeof(object);
var type = module.DefineType("Circle", TypeAttributes.Public, baseType);
var propertyType = typeof(double);
var radiusProperty = type.DefineProperty("Radius", PropertyAttributes.None, propertyType, null);
var backingField = type.DefineField("<Radius>k__BackingField", propertyType, FieldAttributes.Private);
var getter = type.DefineMethod("get_Radius", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, propertyType, null);
var getterIl = getter.GetILGenerator();
getterIl.Emit(OpCodes.Ldarg_0);
getterIl.Emit(OpCodes.Ldfld, backingField);
getterIl.Emit(OpCodes.Ret);
radiusProperty.SetGetMethod(getter);
var setter = type.DefineMethod("set_Radius", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName, null, new Type[] { propertyType });
var valueParameter = setter.DefineParameter(1, ParameterAttributes.None, "value");
var setterIl = setter.GetILGenerator();
setterIl.Emit(OpCodes.Ldarg_0);
setterIl.Emit(OpCodes.Ldarg_1);
setterIl.Emit(OpCodes.Stfld, backingField);
setterIl.Emit(OpCodes.Ret);
radiusProperty.SetSetMethod(setter);
Func<double> circleAreaExpression = () => Math.PI * Math.Pow((double)radiusProperty.GetValue(this), 2);
var or = new OperationReader(circleAreaExpression.Method);
var frame = new StackFrame();
var body = frame.GetMethod().GetMethodBody();
var locals = body.LocalVariables;
检查匿名方法的中间语言表明它包含Load Field radiusProperty操作,其中
radiusProperty是我们新创建的PropertyBuilder,保存为一些匿名的字段class.
or.Operations {System.Reflection.Operation[11]} System.Reflection.Operation[]
+[0] {0000 : ldc.r81414344187}System.Reflection.Operation
+[1]{0009 : ldarg.0} System.Reflection.Operation
+[2]{0010 : ldfld System.Reflection.Emit.PropertyBuilder MethodTest.Program+<>c__DisplayClass0_0::radiusProperty}
System.Reflection.Operation
+[3]{0015 : ldarg.0} System.Reflection.Operation
+[4]{0016 : ldfld MethodTest.Program MethodTest.Program+<>c__DisplayClass0_0::<>4__this}
System.Reflection.Operation
+[5]{0021 : callvirt instance System.Object System.Reflection.PropertyInfo::GetValue()}
System.Reflection.Operation
+[6] {0026 : unbox.any System.Double} System.Reflection.Operation
+[7] {0031 : ldc.r81073741824} System.Reflection.Operation
+[8] {0040 : call System.Double System.Math::Pow()} System.Reflection.Operation
+[9] {0045 : mul} System.Reflection.Operation
+[10] {0046 : ret} System.Reflection.Operation
我们现在想要实现的是,得到我们原来的PropertyBuilder,它是提到的匿名字段class。浏览我们当前所在的方法体的局部变量可以看出,有趣的匿名 class 自豪地占据了局部变量数组的第一位。尽管我们没有对该匿名 class 实例的任何引用。 关于如何获取对有趣的 PropertyBuilder 的引用的任何提示?
locals Count = 19
System.Collections.Generic.IList
{System.Collections.ObjectModel.ReadOnlyCollection}
+[0] {MethodTest.Program+<>c__DisplayClass0_0 (0)}
System.Reflection.LocalVariableInfo>
+[1] {System.Reflection.AssemblyName (1)}
System.Reflection.LocalVariableInfo
+[2] {System.AppDomain (2)} System.Reflection.LocalVariableInfo
+[3] {System.Reflection.Emit.AssemblyBuilder (3)}
System.Reflection.LocalVariableInfo>
+[4] {System.Reflection.Emit.ModuleBuilder (4)}
System.Reflection.LocalVariableInfo>
+[5] {System.Type (5)} System.Reflection.LocalVariableInfo
+[6] {System.Reflection.Emit.TypeBuilder (6)}
System.Reflection.LocalVariableInfo
+[7] {System.Type (7)} System.Reflection.LocalVariableInfo
+[8] {System.Reflection.Emit.FieldBuilder (8)}
System.Reflection.LocalVariableInfo
+[9] {System.Reflection.Emit.MethodBuilder (9)}
System.Reflection.LocalVariableInfo
+[10] {System.Reflection.Emit.ILGenerator (10)}
System.Reflection.LocalVariableInfo
+[11] {System.Reflection.Emit.MethodBuilder (11)}
System.Reflection.LocalVariableInfo
+[12] {System.Reflection.Emit.ParameterBuilder (12)}
System.Reflection.LocalVariableInfo
+[13] {System.Reflection.Emit.ILGenerator (13)}
System.Reflection.LocalVariableInfo
+[14] {System.Func``1[System.Double] (14)}
System.Reflection.LocalVariableInfo
+[15] {System.Reflection.OperationReader (15)}
System.Reflection.LocalVariableInfo
+[16] {System.Diagnostics.StackFrame (16)}
System.Reflection.LocalVariableInfo
+[17] {System.Reflection.MethodBody (17)}
System.Reflection.LocalVariableInfo
+[18] {System.Collections.Generic.IList`1[System.Reflection.LocalVariableInfo]
(18)} System.Reflection.LocalVariableInfo
由于您不能真正确定匿名对象的属性,我建议您将方法 select 和 属性 移动到与对象一起传入的函数。
static void Main(string[] args)
{
var area = ScaryMethod(new { rad = 3 }, t => t.rad);
}
public static double ScaryMethod<T>(T input, Func<T, double> radiusSelector)
{
var radius = radiusSelector(input);
return Math.PI * Math.Pow(radius, 2);
}
依赖反射并不是一个好主意。
我不完全确定这是否是您想要的,但是通过反射从匿名类型中获取 PropertyBuilder 实例非常简单。假设只有一个这种类型的字段,您可以使用此代码:
var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly;
var pBuilder =
circleAreaExpression.Target.GetType()
.GetFields(flags)
.Single(y => y.FieldType == typeof (PropertyBuilder))
.GetValue(circleAreaExpression.Target);
让我们成像,我们发出一个 class 代表圆圈。我们定义了一个双 属性 表示它的半径,具有相关的支持字段和 get/set 访问器。然后我们准备一个计算区域的逻辑,以匿名方法的形式,使用新创建的PropertyBuilder。
var assemblyName = new AssemblyName();
assemblyName.Name = "SampleAssembly";
var domain = Thread.GetDomain();
var assembly = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
var module = assembly.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll", true);
var baseType = typeof(object);
var type = module.DefineType("Circle", TypeAttributes.Public, baseType);
var propertyType = typeof(double);
var radiusProperty = type.DefineProperty("Radius", PropertyAttributes.None, propertyType, null);
var backingField = type.DefineField("<Radius>k__BackingField", propertyType, FieldAttributes.Private);
var getter = type.DefineMethod("get_Radius", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, propertyType, null);
var getterIl = getter.GetILGenerator();
getterIl.Emit(OpCodes.Ldarg_0);
getterIl.Emit(OpCodes.Ldfld, backingField);
getterIl.Emit(OpCodes.Ret);
radiusProperty.SetGetMethod(getter);
var setter = type.DefineMethod("set_Radius", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName, null, new Type[] { propertyType });
var valueParameter = setter.DefineParameter(1, ParameterAttributes.None, "value");
var setterIl = setter.GetILGenerator();
setterIl.Emit(OpCodes.Ldarg_0);
setterIl.Emit(OpCodes.Ldarg_1);
setterIl.Emit(OpCodes.Stfld, backingField);
setterIl.Emit(OpCodes.Ret);
radiusProperty.SetSetMethod(setter);
Func<double> circleAreaExpression = () => Math.PI * Math.Pow((double)radiusProperty.GetValue(this), 2);
var or = new OperationReader(circleAreaExpression.Method);
var frame = new StackFrame();
var body = frame.GetMethod().GetMethodBody();
var locals = body.LocalVariables;
检查匿名方法的中间语言表明它包含Load Field radiusProperty操作,其中 radiusProperty是我们新创建的PropertyBuilder,保存为一些匿名的字段class.
or.Operations {System.Reflection.Operation[11]} System.Reflection.Operation[]
+[0] {0000 : ldc.r81414344187}System.Reflection.Operation
+[1]{0009 : ldarg.0} System.Reflection.Operation
+[2]{0010 : ldfld System.Reflection.Emit.PropertyBuilder MethodTest.Program+<>c__DisplayClass0_0::radiusProperty}
System.Reflection.Operation+[3]{0015 : ldarg.0} System.Reflection.Operation
+[4]{0016 : ldfld MethodTest.Program MethodTest.Program+<>c__DisplayClass0_0::<>4__this} System.Reflection.Operation
+[5]{0021 : callvirt instance System.Object System.Reflection.PropertyInfo::GetValue()}
System.Reflection.Operation+[6] {0026 : unbox.any System.Double} System.Reflection.Operation
+[7] {0031 : ldc.r81073741824} System.Reflection.Operation
+[8] {0040 : call System.Double System.Math::Pow()} System.Reflection.Operation
+[9] {0045 : mul} System.Reflection.Operation
+[10] {0046 : ret} System.Reflection.Operation
我们现在想要实现的是,得到我们原来的PropertyBuilder,它是提到的匿名字段class。浏览我们当前所在的方法体的局部变量可以看出,有趣的匿名 class 自豪地占据了局部变量数组的第一位。尽管我们没有对该匿名 class 实例的任何引用。 关于如何获取对有趣的 PropertyBuilder 的引用的任何提示?
locals Count = 19 System.Collections.Generic.IList {System.Collections.ObjectModel.ReadOnlyCollection}
+[0] {MethodTest.Program+<>c__DisplayClass0_0 (0)}
System.Reflection.LocalVariableInfo>+[1] {System.Reflection.AssemblyName (1)}
System.Reflection.LocalVariableInfo+[2] {System.AppDomain (2)} System.Reflection.LocalVariableInfo
+[3] {System.Reflection.Emit.AssemblyBuilder (3)} System.Reflection.LocalVariableInfo>
+[4] {System.Reflection.Emit.ModuleBuilder (4)}
System.Reflection.LocalVariableInfo>+[5] {System.Type (5)} System.Reflection.LocalVariableInfo
+[6] {System.Reflection.Emit.TypeBuilder (6)}
System.Reflection.LocalVariableInfo+[7] {System.Type (7)} System.Reflection.LocalVariableInfo
+[8] {System.Reflection.Emit.FieldBuilder (8)}
System.Reflection.LocalVariableInfo+[9] {System.Reflection.Emit.MethodBuilder (9)}
System.Reflection.LocalVariableInfo+[10] {System.Reflection.Emit.ILGenerator (10)}
System.Reflection.LocalVariableInfo+[11] {System.Reflection.Emit.MethodBuilder (11)} System.Reflection.LocalVariableInfo
+[12] {System.Reflection.Emit.ParameterBuilder (12)}
System.Reflection.LocalVariableInfo+[13] {System.Reflection.Emit.ILGenerator (13)}
System.Reflection.LocalVariableInfo+[14] {System.Func``1[System.Double] (14)} System.Reflection.LocalVariableInfo
+[15] {System.Reflection.OperationReader (15)}
System.Reflection.LocalVariableInfo+[16] {System.Diagnostics.StackFrame (16)} System.Reflection.LocalVariableInfo
+[17] {System.Reflection.MethodBody (17)} System.Reflection.LocalVariableInfo
+[18] {System.Collections.Generic.IList`1[System.Reflection.LocalVariableInfo] (18)} System.Reflection.LocalVariableInfo
由于您不能真正确定匿名对象的属性,我建议您将方法 select 和 属性 移动到与对象一起传入的函数。
static void Main(string[] args)
{
var area = ScaryMethod(new { rad = 3 }, t => t.rad);
}
public static double ScaryMethod<T>(T input, Func<T, double> radiusSelector)
{
var radius = radiusSelector(input);
return Math.PI * Math.Pow(radius, 2);
}
依赖反射并不是一个好主意。
我不完全确定这是否是您想要的,但是通过反射从匿名类型中获取 PropertyBuilder 实例非常简单。假设只有一个这种类型的字段,您可以使用此代码:
var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly;
var pBuilder =
circleAreaExpression.Target.GetType()
.GetFields(flags)
.Single(y => y.FieldType == typeof (PropertyBuilder))
.GetValue(circleAreaExpression.Target);