Click here to Skip to main content
15,867,895 members
Articles / Programming Languages / C#
Article

Casting and Passing Anonymous Types

Rate me:
Please Sign up or sign in to vote.
4.38/5 (3 votes)
17 Jan 2008CPOL3 min read 68.3K   21   5
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:

C#
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:

C#
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:

  1. Property names are case-insensitive. The source object may include a property Foo, while the example object may be built to expect foo. These two are considered the same property and Foo is therefore copied to foo. Good for the VB guys and gals, and doesn't break code when Fullname gets renamed to FullName.
  2. Properties are optional. A property included in the source object doesn't necessarily have to be included in the example class, and vice-versa.
  3. Property types are not explicit. The example object could include a property Foo of type string, while the source object includes a property Foo of type int. The property gets converted to a string 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:

C#
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer Tiscali Netherlands
Netherlands Netherlands
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionen nu zonder reflection Pin
patt24-Jan-08 3:58
patt24-Jan-08 3:58 
GeneralRe: en nu zonder reflection Pin
thany.nl24-Jan-08 7:59
thany.nl24-Jan-08 7:59 
GeneralC# Does have Duck Typing! You just proved it! Pin
Jay R. Wren23-Jan-08 13:11
Jay R. Wren23-Jan-08 13:11 
GeneralInteresting approach Pin
MR_SAM_PIPER22-Jan-08 12:16
MR_SAM_PIPER22-Jan-08 12:16 
It's a very clever solution to a problem that shouldn't really exist though - ideally anonymous types should never be passed outside a local scope by design as they are by definition not strongly-typed, thus they weaken the integrity of the design-time environment.

I see anonymous types as convenient only in very specific situations - where I require a custom projection from a LINQ statement that is used solely within the local scope, such as setting a data source on a control or performing a complex calculation. If I need to expose the custom projection outside the local scope, then I use a well-known type for that projection instead.

My goal is to always leverage the capabilities of the design-time environment and the compiler to protect the integrity of code, even if that means adding a few dummy classes here and there. In practice, I haven't encountered a problem yet with clutter from such types for custom projections, whereas not knowing the type of object being returned from a method has always been a nightmare.
GeneralThanks Pin
Dewey17-Jan-08 9:19
Dewey17-Jan-08 9:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.