How do I reflect over the members of dynamic object?

C#DynamicReflection

C# Problem Overview


I need to get a dictionary of properties and their values from an object declared with the dynamic keyword in .NET 4? It seems using reflection for this will not work.

Example:

dynamic s = new ExpandoObject();
s.Path = "/Home";
s.Name = "Home";

// How do I enumerate the Path and Name properties and get their values?
IDictionary<string, object> propertyValues = ???

C# Solutions


Solution 1 - C#

In the case of ExpandoObject, the ExpandoObject class actually implements IDictionary<string, object> for its properties, so the solution is as trivial as casting:

IDictionary<string, object> propertyValues = (IDictionary<string, object>)s;

Note that this will not work for general dynamic objects. In these cases you will need to drop down to the DLR via IDynamicMetaObjectProvider.

Solution 2 - C#

There are several scenarios to consider. First of all, you need to check the type of your object. You can simply call GetType() for this. If the type does not implement IDynamicMetaObjectProvider, then you can use reflection same as for any other object. Something like:

var propertyInfo = test.GetType().GetProperties();

However, for IDynamicMetaObjectProvider implementations, the simple reflection doesn't work. Basically, you need to know more about this object. If it is ExpandoObject (which is one of the IDynamicMetaObjectProvider implementations), you can use the answer provided by itowlson. ExpandoObject stores its properties in a dictionary and you can simply cast your dynamic object to a dictionary.

If it's DynamicObject (another IDynamicMetaObjectProvider implementation), then you need to use whatever methods this DynamicObject exposes. DynamicObject isn't required to actually "store" its list of properties anywhere. For example, it might do something like this (I'm reusing an example from my blog post):

public class SampleObject : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = binder.Name;
        return true;
    }
}

In this case, whenever you try to access a property (with any given name), the object simply returns the name of the property as a string.

dynamic obj = new SampleObject();
Console.WriteLine(obj.SampleProperty);
//Prints "SampleProperty".

So, you don't have anything to reflect over - this object doesn't have any properties, and at the same time all valid property names will work.

I'd say for IDynamicMetaObjectProvider implementations, you need to filter on known implementations where you can get a list of properties, such as ExpandoObject, and ignore (or throw an exception) for the rest.

Solution 3 - C#

If the IDynamicMetaObjectProvider can provide the dynamic member names, you can get them. See GetMemberNames implementation in the apache licensed PCL library Dynamitey (which can be found in nuget), it works for ExpandoObjects and DynamicObjects that implement GetDynamicMemberNames and any other IDynamicMetaObjectProvider who provides a meta object with an implementation of GetDynamicMemberNames without custom testing beyond is IDynamicMetaObjectProvider.

After getting the member names it's a little more work to get the value the right way, but Impromptu does this but it's harder to point to just the interesting bits and have it make sense. Here's the documentation and it is equal or faster than reflection, however, unlikely to be faster than a dictionary lookup for expando, but it works for any object, expando, dynamic or original - you name it.

Solution 4 - C#

Requires Newtonsoft Json.Net

A little late, but I came up with this. It gives you just the keys and then you can use those on the dynamic:

public List<string> GetPropertyKeysForDynamic(dynamic dynamicToGetPropertiesFor)
{
    JObject attributesAsJObject = dynamicToGetPropertiesFor;
    Dictionary<string, object> values = attributesAsJObject.ToObject<Dictionary<string, object>>();
    List<string> toReturn = new List<string>();
    foreach (string key in values.Keys)
    {
        toReturn.Add(key);                
    }
    return toReturn;
}

Then you simply foreach like this:

foreach(string propertyName in GetPropertyKeysForDynamic(dynamicToGetPropertiesFor))
{
    dynamic/object/string propertyValue = dynamicToGetPropertiesFor[propertyName];
    // And
    dynamicToGetPropertiesFor[propertyName] = "Your Value"; // Or an object value
}

Choosing to get the value as a string or some other object, or do another dynamic and use the lookup again.

Solution 5 - C#

Simple solution to only reflect over json objects:

using System.Collections.Generic;
using System.Text.Json;
dynamic d = new {a = 1, b = 2, c = 3};
foreach ((string k, object o) in JsonSerializer.Deserialize<Dictionary<string, object>>(JsonSerializer.Serialize(d)))
{

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFlatliner DOAView Question on Stackoverflow
Solution 1 - C#itowlsonView Answer on Stackoverflow
Solution 2 - C#Alexandra RusinaView Answer on Stackoverflow
Solution 3 - C#jbtuleView Answer on Stackoverflow
Solution 4 - C#Mr. BView Answer on Stackoverflow
Solution 5 - C#trinalbadger587View Answer on Stackoverflow