This question already has an answer here:
-
How do I reflect over the members of dynamic object?
4 answers
Look at the following example:
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();
}
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.
How do I create a runtime object, that is dynamic and will also work normally with reflection, like the GetProperties
method?
EDIT
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.
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.
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 );
You don't need reflection with ExpandoObject
. It's really just a fancy implementation of IDictionary<string, object>
. You can cast it as such, and then you will have a dictionary where the keys are the property names and the values are the property values:
// 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();
Another option would be to use the features of an IDynamicMetaObjectProvider
, which ExpandoObject
also implements. Usage would look like:
var metaObjectProvider = (IDynamicMetaObjectProvider)myObject2;
var propertyNames = metaObjectProvider
.GetMetaObject(Expression.Constant(metaObjectProvider))
.GetDynamicMemberNames();
In this case propertyNames
would be an IEnumerable<string>
with the dynamic member names.
Your first example is an anonymous object. The compiler actually generates a type behind the scenes that you can reflect on.
The second example uses an ExpandoObject
to back your dynamic object. ExpandoObject
has no properties of its own which is why your call returns what it does. ExpandoObject
does explicitly implement IDictionary<string, object>
that gives you access to the properties and their values. You can use it as follows:
var properties2 = (myObject2 as IDictionary<string, object>).Keys;
ExpandoObject
is what is special here, nothing else. It's not actually mutating the definition of the type itself at runtime. What's actually going on here is that the apparent property accesses are actually mutating an IDictionary<string,object>
that is held behind the scenes. To access the properties of an ExpandoObject
(note this won't work for any other type) you can cast it to IDictionary<string,object>
, which is the intended mechanism of getting that data from an ExpandoObject
.