How do I reflect over the members of dynamic object?

115,208

Solution 1

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 2

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 3

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 4

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

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

}
Share:
115,208
Flatliner DOA
Author by

Flatliner DOA

C# Developer with extensive experience with ASP.NET Web Forms, WCF, WPF, Silverlight, Javascript and Xml

Updated on August 30, 2021

Comments

  • Flatliner DOA
    Flatliner DOA almost 3 years

    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 = ???
    
  • Flatliner DOA
    Flatliner DOA about 14 years
    Thanks for that, unfortunately the sample was a little oversimplified. I need to be able to inspect a dynamic object without knowing what it's real type is.
  • Flatliner DOA
    Flatliner DOA about 14 years
    It just seems to me that if the type I consume is Dynamic then I shouldn't be making assumptions about it's underlying type. I should be able to call GetDynamicMemberNames to get the list of members. It seems stupid that static types have better runtime inspection support than dynamic ones!
  • Alexandra Rusina
    Alexandra Rusina about 14 years
    You can override the GetDynamicMemberNames() method for a dynamic object to return the list names of dynamic members. The problem is that it's not guaranteed that every dynamic object has this method (ExpandoObject doesn't). It's not surprising that reflection works better for static types. It was created for them in the first place. With dynamics, you have to rely more on unit-testing and TDD.
  • Flatliner DOA
    Flatliner DOA over 11 years
    Thanks to get to the point the code is: var tTarget = target as IDynamicMetaObjectProvider; if (tTarget !=null) { tList.AddRange(tTarget.GetMetaObject(Expression.Constant(tTa‌​rget)).GetDynamicMem‌​berNames()); }
  • NicoJuicy
    NicoJuicy over 10 years
    The MSDN Documentation for GetDynamicMemberNames() mentions: "This method exists for debugging purposes only.", not really comforting :(
  • JJS
    JJS almost 8 years
    thanks for your great library. I was having issues round-tripping a dynamic object to this library dynamicjson.codeplex.com, and was able to extended it with your library.
  • aloisdg
    aloisdg over 7 years
    You dont need the list toReturn.
  • Demodave
    Demodave about 7 years
    example please.
  • Sharique Abdullah
    Sharique Abdullah almost 7 years
    This only works for objects of the ExpandoObject class, the DynamicObject class is another extensible class which doesn't implement IDictionary but rather implements IDynamicMetaObjectProvider.
  • Sharique Abdullah
    Sharique Abdullah almost 7 years
    It does answer the OP's question though.
  • k25
    k25 over 6 years
    Perfect! I had to change JObject attributesAsJObject = dynamicToGetPropertiesFor; to Object attributesAsJObject = (JObject)JToken.FromObject(obj); though!!
  • Flydog57
    Flydog57 almost 6 years
    Just to reiterate what @k25 said. You need to replace JObject attributesAsJObject = dynamicToGetPropertiesFor; with something like: var jObject = (JObject) JToken.FromObject(dynamicToGetPropertiesFor);. At that point, you can get a dictionary of property names and values by doing something like var objProperties = jObject.ToObject<Dictionary<string, object>>();. With that in hand, you are off to the races. This doesn't need a dynamic. It works fine with anything that is a subclass of DynamicObject