How do I iterate over the properties of an anonymous object in C#?
Asked Answered
E

7

68

I want to take an anonymous object as argument to a method, and then iterate over its properties to add each property/value to a a dynamic ExpandoObject.

So what I need is to go from

new { Prop1 = "first value", Prop2 = SomeObjectInstance, Prop3 = 1234 }

to knowing names and values of each property, and being able to add them to the ExpandoObject.

How do I accomplish this?

Side note: This will be done in many of my unit tests (I'm using it to refactor away a lot of junk in the setup), so performance is to some extent relevant. I don't know enough about reflection to say for sure, but from what I've understood it's pretty performance heavy, so if it's possible I'd rather avoid it...

Follow-up question: As I said, I'm taking this anonymous object as an argument to a method. What datatype should I use in the method's signature? Will all properties be available if I use object?

Eversion answered 7/4, 2010 at 17:23 Comment(1)
Reflection performance really isn't too terrible. If you have a very large number of instances you need to do this to, you can cache the PropertyInfo entries for a given anonymous type and then reiterate over those PropertyInfo entries to resolve the properties for each instance. You could even create delegates for the GetMethod for each property and cache those.Obvert
C
89
foreach(var prop in myVar.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
   Console.WriteLine("Name: {0}, Value: {1}",prop.Name, prop.GetValue(myVar,null));
}
Cacciatore answered 7/4, 2010 at 17:26 Comment(4)
OK, I have to use reflection. Will this be a performance issue? Also, how to I get the name of the property in a form that I can use to add to the ExpandoObject?Eversion
I solved the problem of the follow up question: (MyExpandoObject as ICollection<KeyValuePair<string, object>>).Add(...) did the trick. Thanks!Eversion
In principle, Reflection should not be needed to do this. If a compile-time looping feature was provided, the same result could be achieved more efficiently, and the introspection could be confined to compile-time.Buffo
Pure genius! Thanks :)Orthopedist
G
9

Reflect on the anonymous object to get its property names and values, then take advantage of an ExpandoObject actually being a dictionary to populate it. Here's an example, expressed as a unit test:

    [TestMethod]
    public void ShouldBeAbleToConvertAnAnonymousObjectToAnExpandoObject()
    {
        var additionalViewData = new {id = "myControlId", css = "hide well"};
        dynamic result = new ExpandoObject();
        var dict = (IDictionary<string, object>)result;
        foreach (PropertyInfo propertyInfo in additionalViewData.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
        {
            dict[propertyInfo.Name] = propertyInfo.GetValue(additionalViewData, null);
        }
        Assert.AreEqual(result.id, "myControlId");
        Assert.AreEqual(result.css, "hide well");
    }
Galina answered 24/7, 2013 at 0:8 Comment(0)
B
4

An alternative approach is to use DynamicObject instead of ExpandoObject, and that way you only have the overhead of doing the reflection if you actually try to access a property from the other object.

public class DynamicForwarder : DynamicObject 
{
    private object _target;

    public DynamicForwarder(object target)
    {
        _target = target;
    }

    public override bool TryGetMember(
        GetMemberBinder binder, out object result)
    {
        var prop = _target.GetType().GetProperty(binder.Name);
        if (prop == null)
        {
            result = null;
            return false;
        }

        result = prop.GetValue(_target, null);
        return true;
    }
}

Now it only does the reflection when you actually try to access the property via a dynamic get. On the downside, if you repeatedly access the same property, it has to do the reflection each time. So you could cache the result:

public class DynamicForwarder : DynamicObject 
{
    private object _target;
    private Dictionary<string, object> _cache = new Dictionary<string, object>();

    public DynamicForwarder(object target)
    {
        _target = target;
    }

    public override bool TryGetMember(
        GetMemberBinder binder, out object result)
    {
        // check the cache first
        if (_cache.TryGetValue(binder.Name, out result))
            return true;

        var prop = _target.GetType().GetProperty(binder.Name);
        if (prop == null)
        {
            result = null;
            return false;
        }

        result = prop.GetValue(_target, null);
        _cache.Add(binder.Name, result); // <-------- insert into cache
        return true;
    }
}

You could support storing a list of target objects to coalesce their properties, and support setting properties (with a similar override called TrySetMember) to allow you to dynamically set values in the cache dictionary.

Of course, the overhead of reflection is probably not going to be worth worrying about, but for large objects this could limit the impact of it. What is maybe more interesting is the extra flexibility it gives you.

Blackfish answered 7/4, 2010 at 17:48 Comment(0)
W
1

This is an old question, but now you should be able to do this with the following code:

dynamic expObj = new ExpandoObject();
    expObj.Name = "James Kirk";
    expObj.Number = 34;

// print the dynamically added properties
// enumerating over it exposes the Properties and Values as a KeyValuePair
foreach (KeyValuePair<string, object> kvp in expObj){ 
    Console.WriteLine("{0} = {1} : Type: {2}", kvp.Key, kvp.Value, kvp.Value.GetType());
}

The output would look like the following:

Name = James Kirk : Type: System.String

Number = 34 : Type: System.Int32

Whisenant answered 22/11, 2016 at 21:4 Comment(1)
The question is about starting from an "anonymous" type; which has its own benefits. Use of an ExpandoObject, to start, is fundamentally not what OP is seeking. Perhaps they don't have control over how the object is created.Agitato
B
0

you have to use reflection.... (code "borrowed" from this url)

using System.Reflection;  // reflection namespace

// get all public static properties of MyClass type
PropertyInfo[] propertyInfos;
propertyInfos = typeof(MyClass).GetProperties(BindingFlags.Public |
                                              BindingFlags.Static);
// sort properties by name
Array.Sort(propertyInfos,
        delegate(PropertyInfo propertyInfo1, PropertyInfo propertyInfo2)
        { return propertyInfo1.Name.CompareTo(propertyInfo2.Name); });

// write property names
foreach (PropertyInfo propertyInfo in propertyInfos)
{
  Console.WriteLine(propertyInfo.Name);
}
Babirusa answered 7/4, 2010 at 17:25 Comment(2)
OK - I have to use reflection. Will this be a performance issue if I do this in most of my unit tests, once per test?Eversion
@Tomas: There's no way to answer that sort of question with that amount of information. Just try it and see.Cambrai
R
0

Use Reflection.Emit to create a generic method to fill an ExpandoObject.

OR use Expressions perhaps (I think this would only be possible in .NET 4 though).

Neither of these approaches uses reflection when invoking, only during setup of a delegate (which obviously needs to be cached).

Here is some Reflection.Emit code to fill a dictionary (I guess ExpandoObject is not far off);

static T CreateDelegate<T>(this DynamicMethod dm) where T : class
{
  return dm.CreateDelegate(typeof(T)) as T;
}

static Dictionary<Type, Func<object, Dictionary<string, object>>> cache = 
   new Dictionary<Type, Func<object, Dictionary<string, object>>>();

static Dictionary<string, object> GetProperties(object o)
{
  var t = o.GetType();

  Func<object, Dictionary<string, object>> getter;

  if (!cache.TryGetValue(t, out getter))
  {
    var rettype = typeof(Dictionary<string, object>);

    var dm = new DynamicMethod(t.Name + ":GetProperties", rettype, 
       new Type[] { typeof(object) }, t);

    var ilgen = dm.GetILGenerator();

    var instance = ilgen.DeclareLocal(t);
    var dict = ilgen.DeclareLocal(rettype);

    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Castclass, t);
    ilgen.Emit(OpCodes.Stloc, instance);

    ilgen.Emit(OpCodes.Newobj, rettype.GetConstructor(Type.EmptyTypes));
    ilgen.Emit(OpCodes.Stloc, dict);

    var add = rettype.GetMethod("Add");

    foreach (var prop in t.GetProperties(
      BindingFlags.Instance |
      BindingFlags.Public))
    {
      ilgen.Emit(OpCodes.Ldloc, dict);

      ilgen.Emit(OpCodes.Ldstr, prop.Name);

      ilgen.Emit(OpCodes.Ldloc, instance);
      ilgen.Emit(OpCodes.Ldfld, prop);
      ilgen.Emit(OpCodes.Castclass, typeof(object));

      ilgen.Emit(OpCodes.Callvirt, add);
    }

    ilgen.Emit(OpCodes.Ldloc, dict);
    ilgen.Emit(OpCodes.Ret);

    cache[t] = getter = 
      dm.CreateDelegate<Func<object, Dictionary<string, object>>>();
  }

  return getter(o);
}
Raspy answered 7/4, 2010 at 17:26 Comment(0)
G
0

In ASP.NET you can use the RouteValueDictionary class to quickly convert an anonymous variable into a properties dictionary.

Internally it uses reflection too, but it also maintains an internal cache of properties. So subsequent calls will be much faster (proof)

So if (!) you are writing an ASP.NET app, then you can use

var d = new RouteValueDictionary(new { A = 1, B = 2 });
d["A"] //equals 1
d["B"] //equals 2

P.S. This class is used every time you write ASP.NET code like this:

Url.Action("Action, "Controller", new { parameter = xxx })
Gi answered 3/6, 2023 at 19:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.