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

这里缺少 classes : link1 link2

由于您不能真正确定匿名对象的属性,我建议您将方法 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);