Casting and Passing Anonymous Types
How to pass anonymous types and how to cast them
Introduction
Since C# 3.0, we've had the chance to play around with anonymous types among other cool new stuff. Since I've starting working with them, I found anonymous types to be useful in more situations than just LINQ statements. For example, in JavaScript many functions may return associative arrays, and in the same way in C#, a method that may return an anonymously typed object when declaring a whole class or struct is overkill.
That's not possible, Microsoft says. You can't pass an anonymous type from one method to the next, down the call stack.
In fact, you can pass anonymous types down the call stack. You'll just have to (implicitly) cast them back to an object
. However, by then, your anonymous type becomes nearly useless.
Background
A fellow who goes by the name AlexJ (kudos for the idea!) has found otherwise. Basically what we need to do is take the returned anonymous type and cast it back to its original form. For this, he uses a generic extension method on object
that takes the type of an example object
and casts the object
to that type:
public static T CastByExample<T>(this object obj, T example) {
return (T) obj;
}
Say we've got a method that returns some anonymous type that has a property FullName
. Nothing more. Then having put that object into obj
, we could use the following call to get back to the original, strong-typed, IntelliSense-enabled object:
var v = obj.CastByExample(new { FullName = "" });
The great disadvantage of this approach is that when the number of properties, names of properties and types of properties of obj
and example do not exactly match up, a nasty InvalidCastException
is thrown at runtime.
A Better, More Flexible Approach
I wanted to get rid of as much of the required coordination between the method that returns an anonymous object
, and its consumer. Since waiting for InvalidCastException
reports from the end-user is a great way to do this, I set out to forge a method that basically copies all properties from a source object
into a new one, based on an example object
.
My design has three advantages over the initial approach:
- Property names are case-insensitive. The source
object
may include a propertyFoo
, while the exampleobject
may be built to expectfoo
. These two are considered the same property andFoo
is therefore copied tofoo
. Good for the VB guys and gals, and doesn't break code whenFullname
gets renamed toFullName
. - Properties are optional. A property included in the source
object
doesn't necessarily have to be included in the example class, and vice-versa. - Property types are not explicit. The example
object
could include a propertyFoo
of typestring
, while the sourceobject
includes a propertyFoo
of typeint
. The property gets converted to astring
automatically.
If a property in the resulting anonymous type cannot be set because it doesn't exist in the source object
, or because it's value cannot be converted, it gets a default value. That's 0
for integral types, null
for reference types, etc.
So without further ado, here's the code:
public static T TolerantCast<T>(this object o, T example) where T: class {
IComparer<string> comparer = StringComparer.CurrentCultureIgnoreCase;
//Get constructor with lowest number of parameters and its parameters
var constructor = typeof(T).GetConstructors(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
).OrderBy(c => c.GetParameters().Length).First();
var parameters = constructor.GetParameters();
//Get properties of input object
var sourceProperties = new List<PropertyInfo>(o.GetType().GetProperties());
if (parameters.Length > 0) {
var values = new object[parameters.Length];
for (int i = 0; i < values.Length; i++) {
Type t = parameters[i].ParameterType;
//See if the current parameter is found as a property in the input object
var source = sourceProperties.Find(delegate(PropertyInfo item) {
return comparer.Compare(item.Name, parameters[i].Name) == 0;
});
//See if the property is found, is readable, and is not indexed
if (source != null && source.CanRead &&
source.GetIndexParameters().Length == 0) {
//See if the types match.
if (source.PropertyType == t) {
//Get the value from the property in the input object and save it for use
//in the constructor call.
values[i] = source.GetValue(o, null);
continue;
}
else {
//See if the property value from the input object can be converted to
//the parameter type
try {
values[i] = Convert.ChangeType(source.GetValue(o, null), t);
continue;
}
catch {
//Impossible. Forget it then.
}
}
}
//If something went wrong (i.e. property not found, or property isn't
//converted/copied), get a default value.
values[i] = t.IsValueType ? Activator.CreateInstance(t) : null;
}
//Call the constructor with the collected values and return it.
return (T) constructor.Invoke(values);
}
//Call the constructor without parameters and return the it.
return (T) constructor.Invoke(null);
}
A Final Note
Anonymous types are in no way a golden hammer. Only use them when you see the alternative being totally overkill. Since this approach uses reflection quite heavily, you shouldn't be using this method if CPU utilization is a bottleneck.
History
- 17th January, 2008: Initial version