如何使.NET反射动态生成的对象一起工作?反射、对象、动态、工作

2023-09-04 09:42:23 作者:小闹心° Forever

请看下面的例子:

void Main()
{
    // APPROACH 1: With an anonymous type
    var myObject = new {
        Property1 = "PropertyValue1"
    };

    // WORKS: Properties contains "Property1"
    var properties = myObject.GetType().GetProperties();

    // APPROACH 2: With an expando object
    dynamic myObject2 = new ExpandoObject();
    myObject2.Property1 = "PropertyValue1";

    // DOES NOT WORK: Properties2 is null or empty
    var properties2 = myObject2.GetType().GetProperties();
}

我要的是让 Type.GetProperties()工作在一个动态生成的类型。我真正明白为什么它工作在方式1,而不是2。事实上,在办法1,编译器有oportunity产生一个匿名类型,看起来完全是一个命名的类型等。在方法2,但是,编译时类型实际上是一个ExpandoObject,所以反射不能正常工作。

What I want is to make Type.GetProperties() to work on a dynamically generated type. I really understand why it works in the approach 1 and not 2. Actually, in the approach 1, the compiler has the oportunity to generate an anonymous type that looks exactly like a named type. In the approach 2, however, the compile time type is actually an ExpandoObject, so reflection doesn't work properly.

如何创建一个运行时的对象,这是动态的,也通常与反思的工作,如的GetProperties,的方法?

How do I create a runtime object, that is dynamic and will also work normally with reflection, like the GetProperties method?

修改

感谢所有的答案,但我真正明白,我知道如何从ExpandoObject键和值。该问题是,我需要一个动态创建的实例传递给方法,我的控制将外,在反过来,调用实例的GetProperties,()。

Thanks for all answers, but I really understand and I know how to get the keys and values from the ExpandoObject.. The problem is that I need to pass a dynamically created instance to a method, outside my control that will, in turn, call GetProperties() on the instance.

推荐答案

为了通过反射一个动态构造类型得到的值,你需要使用 Reflection.Emit的。这是不理想的,因为它需要你正确地发出MSIL。如果您使用的情况下,只需要简单的属性访问,那么它可能是可行的,但不明智的。

In order to get the values via reflection for a dynamically-constructed type, you'll need to use Reflection.Emit. This is less than ideal as it requires you to properly emit MSIL. If your use case requires only simple property access, then it may be feasible, though ill-advised.

下面是一个简单的,轻测试,类型,使用 Reflection.Emit的生成器:

Here's a simple, lightly-tested, type builder that uses Reflection.Emit:

    public static class TypeBuilderUtil {
        public static Type BuildDynamicType() {
            var typeBuilder = CreateTypeBuilder( "DynamicType" );
            CreateProperty( typeBuilder, "Property1", typeof ( string ) );

            var objectType = typeBuilder.CreateType();
            return objectType;
        }

        private static TypeBuilder CreateTypeBuilder( string typeName ) {
            var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( new AssemblyName( "DynamicAssembly" ), AssemblyBuilderAccess.Run );
            var moduleBuilder = assemblyBuilder.DefineDynamicModule( "DynamicModule" );
            var typeBuilder = moduleBuilder.DefineType( typeName,
                                                        TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout
                                                        , null );
            return typeBuilder;
        }

        private static void CreateProperty( TypeBuilder typeBuilder, string propertyName, Type propertyType ) {
            var backingFieldBuilder = typeBuilder.DefineField( "_" + propertyName, propertyType, FieldAttributes.Private );
            var propertyBuilder = typeBuilder.DefineProperty( propertyName, PropertyAttributes.HasDefault, propertyType, null );
            // Build setter
            var getterMethodBuilder = typeBuilder.DefineMethod( "get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes );
            var getterIl = getterMethodBuilder.GetILGenerator();
            getterIl.Emit( OpCodes.Ldarg_0 );
            getterIl.Emit( OpCodes.Ldfld, backingFieldBuilder );
            getterIl.Emit( OpCodes.Ret );

            // Build setter
            var setterMethodBuilder = typeBuilder.DefineMethod( "set_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new[] {propertyType} );
            var setterIl = setterMethodBuilder.GetILGenerator();
            setterIl.Emit( OpCodes.Ldarg_0 );
            setterIl.Emit( OpCodes.Ldarg_1 );
            setterIl.Emit( OpCodes.Stfld, backingFieldBuilder );
            setterIl.Emit( OpCodes.Ret );

            propertyBuilder.SetGetMethod( getterMethodBuilder );
            propertyBuilder.SetSetMethod( setterMethodBuilder );
        }
    }

您将创建类型,然后填充它是这样:

You would create the type, then populate it as such:

        var myType = TypeBuilderUtil.BuildDynamicType();
        var myObject = Activator.CreateInstance( myType );

        // Set the value
        var propertyInfo = myObject.GetType().GetProperty( "Property1", BindingFlags.Instance | BindingFlags.Public );
        propertyInfo.SetValue( myObject, "PropertyValue", null );