How to access property of anonymous type in C#?

135,376

Solution 1

If you want a strongly typed list of anonymous types, you'll need to make the list an anonymous type too. The easiest way to do this is to project a sequence such as an array into a list, e.g.

var nodes = (new[] { new { Checked = false, /* etc */ } }).ToList();

Then you'll be able to access it like:

nodes.Any(n => n.Checked);

Because of the way the compiler works, the following then should also work once you have created the list, because the anonymous types have the same structure so they are also the same type. I don't have a compiler to hand to verify this though.

nodes.Add(new { Checked = false, /* etc */ });

Solution 2

If you're storing the object as type object, you need to use reflection. This is true of any object type, anonymous or otherwise. On an object o, you can get its type:

Type t = o.GetType();

Then from that you look up a property:

PropertyInfo p = t.GetProperty("Foo");

Then from that you can get a value:

object v = p.GetValue(o, null);

This answer is long overdue for an update for C# 4:

dynamic d = o;
object v = d.Foo;

And now another alternative in C# 6:

object v = o?.GetType().GetProperty("Foo")?.GetValue(o, null);

Note that by using ?. we cause the resulting v to be null in three different situations!

  1. o is null, so there is no object at all
  2. o is non-null but doesn't have a property Foo
  3. o has a property Foo but its real value happens to be null.

So this is not equivalent to the earlier examples, but may make sense if you want to treat all three cases the same.

To use dynamic to read properties of anonymous type in your Unit Tests, You need to tell your project's compiler services to make the assembly visible internally to your test project. You can add the following into your the project (.proj) file. Refer this link for more information.

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
        <_Parameter1>Name of your test project</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

Solution 3

You could iterate over the anonymous type's properties using Reflection; see if there is a "Checked" property and if there is then get its value.

See this blog post: http://blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx

So something like:

foreach(object o in nodes)
{
    Type t = o.GetType();

    PropertyInfo[] pi = t.GetProperties(); 

    foreach (PropertyInfo p in pi)
    {
        if (p.Name=="Checked" && !(bool)p.GetValue(o))
            Console.WriteLine("awesome!");
    }
}

Solution 4

The accepted answer correctly describes how the list should be declared and is highly recommended for most scenarios.

But I came across a different scenario, which also covers the question asked. What if you have to use an existing object list, like ViewData["htmlAttributes"] in MVC? How can you access its properties (they are usually created via new { @style="width: 100px", ... })?

For this slightly different scenario I want to share with you what I found out. In the solutions below, I am assuming the following declaration for nodes:

List<object> nodes = new List<object>();

nodes.Add(
new
{
    Checked = false,
    depth = 1,
    id = "div_1" 
});

Now you have a list of objects. How can you access the properties within the objects, for example, return a list of all nodes where the Checked property is false?

1. Solution with dynamic

In C# 4.0 and higher versions, you can simply cast to dynamic and write:

if (nodes.Any(n => ((dynamic)n).Checked == false))
    Console.WriteLine("found a  not checked  element!");

Note: This is using late binding, which means it will recognize only at runtime if the object doesn't have a Checked property and throws a RuntimeBinderException in this case - so if you try to use a non-existing Checked2 property you would get the following message at runtime: "'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'".

2. Solution with reflection

The solution with reflection works both with old and new C# compiler versions. For old C# versions please regard the hint at the end of this answer.

Background

As a starting point, I found a good answer here. The idea is to convert the anonymous data type into a dictionary by using reflection. The dictionary makes it easy to access the properties, since their names are stored as keys (you can access them like myDict["myProperty"]).

Inspired by the code in the link above, I created an extension class providing GetProp, UnanonymizeProperties and UnanonymizeListItems as extension methods, which simplify access to anonymous properties. With this class you can simply do the query as follows:

if (nodes.UnanonymizeListItems().Any(n => (bool)n["Checked"] == false))
{
    Console.WriteLine("found a  not checked  element!");
}

or you can use the expression nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any() as if condition, which filters implicitly and then checks if there are any elements returned.

To get the first object containing "Checked" property and return its property "depth", you can use:

var depth = nodes.UnanonymizeListItems()
             ?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");

or shorter: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];

Note: If you have a list of objects which don't necessarily contain all properties (for example, some do not contain the "Checked" property), and you still want to build up a query based on "Checked" values, you can do this:

if (nodes.UnanonymizeListItems(x => { var y = ((bool?)x.GetProp("Checked", true)); 
                                      return y.HasValue && y.Value == false;}).Any())
{
    Console.WriteLine("found a  not checked   element!");
}

This prevents, that a KeyNotFoundException occurs if the "Checked" property does not exist.


The class below contains the following extension methods:

  • UnanonymizeProperties: Is used to de-anonymize the properties contained in an object. This method uses reflection. It converts the object into a dictionary containing the properties and its values.
  • UnanonymizeListItems: Is used to convert a list of objects into a list of dictionaries containing the properties. It may optionally contain a lambda expression to filter beforehand.
  • GetProp: Is used to return a single value matching the given property name. Allows to treat not-existing properties as null values (true) rather than as KeyNotFoundException (false)

For the examples above, all that is required is that you add the extension class below:

public static class AnonymousTypeExtensions
{
    // makes properties of object accessible 
    public static IDictionary UnanonymizeProperties(this object obj)
    {
        Type type = obj?.GetType();
        var properties = type?.GetProperties()
               ?.Select(n => n.Name)
               ?.ToDictionary(k => k, k => type.GetProperty(k).GetValue(obj, null));
        return properties;
    }
    
    // converts object list into list of properties that meet the filterCriteria
    public static List<IDictionary> UnanonymizeListItems(this List<object> objectList, 
                    Func<IDictionary<string, object>, bool> filterCriteria=default)
    {
        var accessibleList = new List<IDictionary>();
        foreach (object obj in objectList)
        {
            var props = obj.UnanonymizeProperties();
            if (filterCriteria == default
               || filterCriteria((IDictionary<string, object>)props) == true)
            { accessibleList.Add(props); }
        }
        return accessibleList;
    }

    // returns specific property, i.e. obj.GetProp(propertyName)
    // requires prior usage of AccessListItems and selection of one element, because
    // object needs to be a IDictionary<string, object>
    public static object GetProp(this object obj, string propertyName, 
                                 bool treatNotFoundAsNull = false)
    {
        try 
        {
            return ((System.Collections.Generic.IDictionary<string, object>)obj)
                   ?[propertyName];
        }
        catch (KeyNotFoundException)
        {
            if (treatNotFoundAsNull) return default(object); else throw;
        }
    }
}

Hint: The code above is using the null-conditional operators, available since C# version 6.0 - if you're working with older C# compilers (e.g. C# 3.0), simply replace ?. by . and ?[ by [ everywhere (and do the null-handling traditionally by using if statements or catch NullReferenceExceptions), e.g.

var depth = nodes.UnanonymizeListItems()
            .FirstOrDefault(n => n.Contains("Checked"))["depth"];

As you can see, the null-handling without the null-conditional operators would be cumbersome here, because everywhere you removed them you have to add a null check - or use catch statements where it is not so easy to find the root cause of the exception resulting in much more - and hard to read - code.

If you're not forced to use an older C# compiler, keep it as is, because using null-conditionals makes null handling much easier.

Note: Like the other solution with dynamic, this solution is also using late binding, but in this case you're not getting an exception - it will simply not find the element if you're referring to a non-existing property, as long as you keep the null-conditional operators.

What might be useful for some applications is that the property is referred to via a string in solution 2, hence it can be parameterized.

Solution 5

Recently, I had the same problem within .NET 3.5 (no dynamic available). Here is how I solved:

// pass anonymous object as argument
var args = new { Title = "Find", Type = typeof(FindCondition) };

using (frmFind f = new frmFind(args)) 
{
...
...
}

Adapted from somewhere on stackoverflow:

// Use a custom cast extension
public static T CastTo<T>(this Object x, T targetType)
{
   return (T)x;
}

Now get back the object via cast:

public partial class frmFind: Form
{
    public frmFind(object arguments)
    {

        InitializeComponent();

        var args = arguments.CastTo(new { Title = "", Type = typeof(Nullable) });

        this.Text = args.Title;

        ...
    }
    ...
}
Share:
135,376

Related videos on Youtube

wgpubs
Author by

wgpubs

Updated on May 31, 2021

Comments

  • wgpubs
    wgpubs almost 3 years

    I have this:

    List<object> nodes = new List<object>(); 
    
    nodes.Add(
    new {
        Checked     = false,
        depth       = 1,
        id          = "div_" + d.Id
    });
    

    ... and I'm wondering if I can then grab the "Checked" property of the anonymous object. I'm not sure if this is even possible. Tried doing this:

    if (nodes.Any(n => n["Checked"] == false)) ... but it doesn't work.

    Thanks

  • Sebastiaan
    Sebastiaan over 13 years
    If you only need one property and you already know its name, there's no point in going through all of them; just use GetProperty and GetValue. Also, System.out.println is Java, not C#...
  • glennkentwell
    glennkentwell over 13 years
    Oops, so it is, Chris! A bit embarrassing...fixed now.
  • Alan
    Alan over 11 years
    Never used a dynamic before till now, nice update for .NET 4.0
  • YaakovHatam
    YaakovHatam over 8 years
    in the c# 4 solution you will get runtime exception if the property aren't exist (object v = d.Foo), while GetValue(o, null) will be null if not exist.
  • Daniel Earwicker
    Daniel Earwicker over 8 years
    No, GetProperty will return null, and GetValue will throw if passed that null, so the overall effect is an exception. The C# 4.0 version gives a more descriptive exception.
  • Scott Gartner
    Scott Gartner over 7 years
    Note that none of these work for an ExpandoObject dynamic object. Calling GetType().GetProperty() only searches for properties on the ExpandoObject itself (since it internally provides overloads for the dynamic access to pull "fake" properties from its internal Dictionary). Here is the additional code: if (o is ExpandoObject) v = ((IDictionary<string, object>)o)["Foo"]; This works because ExpandoObject implements IDictionary and provides access to its internal dictionary.
  • Daniel Earwicker
    Daniel Earwicker over 7 years
    @ScottGartner the dynamic example above works for ExpandoObject, such as dynamic o = new ExpandoObject(); o.Foo = "Something";
  • Scott Gartner
    Scott Gartner over 7 years
    @DanielEarwicker, sorry I was a little overarching in my message. You are certainly correct that specifying the property directly on the object works (the compiler figures out how to access the property at runtime based on the type). It was the GetProperty() specifically that doesn't work and at the time I wrote that reply, I was working on library methods that had to handle regular, dynamic, and Expando objects that were created outside of my control, so I had no way of specifying the access as o.Foo. In the end I had to handle them as a special case.
  • Sarath
    Sarath over 7 years
    If you are using dynamic in different assembly than the source then you need to use [InternalsVisibleTo]
  • Daniel Earwicker
    Daniel Earwicker over 7 years
    @Sarath - only if the property you are accessing is not public, right?
  • Sarath
    Sarath over 7 years
    @DanielEarwicker thanks for completion. It also applies to anonymous types. As all the properties generated for anonymous types are internal.
  • Jijie Chen
    Jijie Chen over 5 years
    If you run into a RuntimeBinder.RuntimeBinderException stating 'object' does not contain a definition some-property, you'll need to add an InternalsVisibleTo attribute in your AssemblyInfo.cs, please see juristr.com/blog/2013/08/object-does-not-contain-definition for more.
  • st_stefanov
    st_stefanov almost 3 years
    The dynamic approach doesn't work in Unit Test unless the test is decorated...