如何使.NET反射与动态生成的对象一起使用?

时间:2014-06-02 20:39:09

标签: c# .net reflection

请看以下示例:

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中,编译器有机会生成一个看起来与命名类型完全相同的匿名类型。但是,在方法2中,编译时类型实际上是一个ExpandoObject,因此反射不能正常工作。

如何创建一个动态的运行时对象,并且还可以通过反射正常工作,如GetProperties方法?

修改

感谢所有答案,但我真的理解并且我知道如何从ExpandoObject获取密钥和值。问题是我需要将一个动态创建的实例传递给一个方法,在我的控制之外,转,在实例上调用GetProperties()。

4 个答案:

答案 0 :(得分:5)

您不需要使用ExpandoObject进行反思。它实际上只是IDictionary<string, object>的精彩实现。你可以这样投,然后你会有一个字典,其中键是属性名称,值是属性值:

// Create your object
dynamic myObject2 = new ExpandoObject();

// Cast to IDictionary<string, object>
var myObjectDictionary = (IDictionary<string, object>)myObject2;

// Get List<string> of properties
var propertyNames = myObjectDictionary.Keys.ToList();

另一个选择是使用IDynamicMetaObjectProvider的功能,ExpandoObject也实现了这些功能。用法如下:

var metaObjectProvider = (IDynamicMetaObjectProvider)myObject2;
var propertyNames = metaObjectProvider
    .GetMetaObject(Expression.Constant(metaObjectProvider))
    .GetDynamicMem‌​berNames();

在这种情况下,propertyNames将是IEnumerable<string>,其中包含动态成员名称。

答案 1 :(得分:4)

为了通过反射为动态构造的类型获取值,您需要使用Reflection.Emit。这不太理想,因为它需要您正确发出MSIL。如果您的用例仅需要简单的属性访问,那么它可能是可行的,尽管不明智。

这是一个使用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 );
        }
    }

您将创建该类型,然后将其填充为:

        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 );

答案 2 :(得分:3)

您的第一个示例是匿名对象。编译器实际上会在幕后生成一个可以反映的类型。

第二个示例使用ExpandoObject来支持您的动态对象。 ExpandoObject没有自己的属性,这就是您的调用返回它的功能的原因。 ExpandoObject明确实现IDictionary<string, object>,可让您访问属性及其值。您可以按如下方式使用它:

var properties2 = (myObject2 as IDictionary<string, object>).Keys;

答案 3 :(得分:2)

ExpandoObject在这里是特别的,没有别的。它实际上并没有在运行时改变类型本身的定义。这里实际发生的是,明显的属性访问实际上是在改变幕后的IDictionary<string,object>。要访问ExpandoObject的属性(请注意,这对任何其他类型都不起作用),您可以将其强制转换为IDictionary<string,object>,这是从{{1}获取数据的预期机制}}