Updated on: May 2, 2020. In C#, an anonymous type is a type (class) without any name that can contain public read-only properties only. It cannot contain other members, such as fields, methods, events, etc. You create an anonymous type using the new operator with an object initializer syntax.
Essentially an anonymous type is a reference type and can be defined using the var keyword. You can have one or more properties in an anonymous type but all of them are read-only. In contrast to a C# class, an anonymous type cannot have a field or a method — it can only have properties.
You create anonymous types by using the new operator together with an object initializer. For more information about object initializers, see Object and Collection Initializers. The following example shows an anonymous type that is initialized with two properties named Amount and Message .
The compiler gives them a name although your application cannot access it. From the perspective of the common language runtime, an anonymous type is no different from any other reference type, except that it cannot be cast to any type except for object.
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!
o
is null
, so there is no object at allo
is non-null
but doesn't have a property Foo
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.
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 */ });
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!");
}
}
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?
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'"
.
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.
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;
...
}
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With