Dynamic Type to do away with Reflection

Posted on Dot net Slackers See other posts from Dot net Slackers
Published on Sat, 22 May 2010 00:00:00 GMT Indexed on 2010/05/22 12:51 UTC
Read the original article Hit count: 6594

Filed under:
The dynamic type in C# 4.0 is a welcome addition to the language. One thing Ive been doing a lot with it is to remove explicit Reflection code thats often necessary when you dynamically need to walk and object hierarchy. In the past Ive had a number of ReflectionUtils that used string based expressions to walk an object hierarchy. With the introduction of dynamic much of the ReflectionUtils code can be removed for cleaner code that runs considerably faster to boot. The old Way - Reflection Heres...

Did you know that DotNetSlackers also publishes .net articles written by top known .net Authors? We already have over 80 articles in several categories including Silverlight. Take a look: here.



Email this Article

© Dot net Slackers or respective owner

Dynamic Type to do away with Reflection

Posted by Rick Strahl on West-Wind See other posts from West-Wind or by Rick Strahl
Published on Sat, 22 May 2010 11:22:59 GMT Indexed on 2010/05/22 11:41 UTC
Read the original article Hit count: 6595

Filed under:
|

The dynamic type in C# 4.0 is a welcome addition to the language. One thing I’ve been doing a lot with it is to remove explicit Reflection code that’s often necessary when you ‘dynamically’ need to walk and object hierarchy. In the past I’ve had a number of ReflectionUtils that used string based expressions to walk an object hierarchy. With the introduction of dynamic much of the ReflectionUtils code can be removed for cleaner code that runs considerably faster to boot.

The old Way - Reflection

Here’s a really contrived example, but assume for a second, you’d want to dynamically retrieve a Page.Request.Url.AbsoluteUrl based on a Page instance in an ASP.NET Web Page request. The strongly typed version looks like this:

string path = Page.Request.Url.AbsolutePath;

Now assume for a second that Page wasn’t available as a strongly typed instance and all you had was an object reference to start with and you couldn’t cast it (right I said this was contrived :-))

If you’re using raw Reflection code to retrieve this you’d end up writing 3 sets of Reflection calls using GetValue(). Here’s some internal code I use to retrieve Property values as part of ReflectionUtils:

/// <summary>
/// Retrieve a property value from an object dynamically. This is a simple version
/// that uses Reflection calls directly. It doesn't support indexers.
/// </summary>
/// <param name="instance">Object to make the call on</param>
/// <param name="property">Property to retrieve</param>
/// <returns>Object - cast to proper type</returns>
public static object GetProperty(object instance, string property)
{
    return instance.GetType().GetProperty(property, ReflectionUtils.MemberAccess).GetValue(instance, null);
}

If you want more control over properties and support both fields and properties as well as array indexers a little more work is required:

/// <summary>
/// Parses Properties and Fields including Array and Collection references.
/// Used internally for the 'Ex' Reflection methods.
/// </summary>
/// <param name="Parent"></param>
/// <param name="Property"></param>
/// <returns></returns>
private static object GetPropertyInternal(object Parent, string Property)
{
    if (Property == "this" || Property == "me")
        return Parent;

    object result = null;
    string pureProperty = Property;
    string indexes = null;
    bool isArrayOrCollection = false;

    // Deal with Array Property
    if (Property.IndexOf("[") > -1)
    {
        pureProperty = Property.Substring(0, Property.IndexOf("["));
        indexes = Property.Substring(Property.IndexOf("["));
        isArrayOrCollection = true;
    }

    // Get the member
    MemberInfo member = Parent.GetType().GetMember(pureProperty, ReflectionUtils.MemberAccess)[0];
    if (member.MemberType == MemberTypes.Property)
        result = ((PropertyInfo)member).GetValue(Parent, null);
    else
        result = ((FieldInfo)member).GetValue(Parent);

    if (isArrayOrCollection)
    {
        indexes = indexes.Replace("[", string.Empty).Replace("]", string.Empty);

        if (result is Array)
        {
            int Index = -1;
            int.TryParse(indexes, out Index);
            result = CallMethod(result, "GetValue", Index);
        }
        else if (result is ICollection)
        {
            if (indexes.StartsWith("\""))
            {
                // String Index
                indexes = indexes.Trim('\"');
                result = CallMethod(result, "get_Item", indexes);
            }
            else
            {
                // assume numeric index
                int index = -1;
                int.TryParse(indexes, out index);
                result = CallMethod(result, "get_Item", index);
            }
        }
    }
    return result;
}
/// <summary>
/// Returns a property or field value using a base object and sub members including . syntax.
/// For example, you can access: oCustomer.oData.Company with (this,"oCustomer.oData.Company")
/// This method also supports indexers in the Property value such as:
/// Customer.DataSet.Tables["Customers"].Rows[0]
/// </summary>
/// <param name="Parent">Parent object to 'start' parsing from. Typically this will be the Page.</param>
/// <param name="Property">The property to retrieve. Example: 'Customer.Entity.Company'</param>
/// <returns></returns>
public static object GetPropertyEx(object Parent, string Property)
{
    Type type = Parent.GetType();

    int at = Property.IndexOf(".");
    if (at < 0)
    {
        // Complex parse of the property    
        return GetPropertyInternal(Parent, Property);
    }

    // Walk the . syntax - split into current object (Main) and further parsed objects (Subs)
    string main = Property.Substring(0, at);
    string subs = Property.Substring(at + 1);

    // Retrieve the next . section of the property
    object sub = GetPropertyInternal(Parent, main);

    // Now go parse the left over sections
    return GetPropertyEx(sub, subs);
}

As you can see there’s a fair bit of code involved into retrieving a property or field value reliably especially if you want to support array indexer syntax. This method is then used by a variety of routines to retrieve individual properties including one called GetPropertyEx() which can walk the dot syntax hierarchy easily.

Anyway with ReflectionUtils I can  retrieve Page.Request.Url.AbsolutePath using code like this:

string url = ReflectionUtils.GetPropertyEx(Page, "Request.Url.AbsolutePath") as string; 

This works fine, but is bulky to write and of course requires that I use my custom routines. It’s also quite slow as the code in GetPropertyEx does all sorts of string parsing to figure out which members to walk in the hierarchy.

Enter dynamic – way easier!

.NET 4.0’s dynamic type makes the above really easy. The following code is all that it takes:

object objPage = Page;     // force to object for contrivance :)
dynamic page = objPage;    // convert to dynamic from untyped object
string scriptUrl = page.Request.Url.AbsolutePath;

The dynamic type assignment in the first two lines turns the strongly typed Page object into a dynamic. The first assignment is just part of the contrived example to force the strongly typed Page reference into an untyped value to demonstrate the dynamic member access. The next line then just creates the dynamic type from the Page reference which allows you to access any public properties and methods easily. It also lets you access any child properties as dynamic types so when you look at Intellisense you’ll see something like this when typing Request.:

Dynamic

In other words any dynamic value access on an object returns another dynamic object which is what allows the walking of the hierarchy chain.

Note also that the result value doesn’t have to be explicitly cast as string in the code above – the compiler is perfectly happy without the cast in this case inferring the target type based on the type being assigned to. The dynamic conversion automatically handles the cast when making the final assignment which is nice making for natural syntnax that looks *exactly* like the fully typed syntax, but is completely dynamic.

Note that you can also use indexers in the same natural syntax so the following also works on the dynamic page instance:

string scriptUrl = page.Request.ServerVariables["SCRIPT_NAME"];

The dynamic type is going to make a lot of Reflection code go away as it’s simply so much nicer to be able to use natural syntax to write out code that previously required nasty Reflection syntax.

Another interesting thing about the dynamic type is that it actually works considerably faster than Reflection. Check out the following methods that check performance:

void Reflection()
     {
         Stopwatch stop = new Stopwatch();
         stop.Start();

         for (int i = 0; i < reps; i++)
         {
            // string url = ReflectionUtils.GetProperty(Page,"Title") as string;// "Request.Url.AbsolutePath") as string;
             string url = Page.GetType().GetProperty("Title", ReflectionUtils.MemberAccess).GetValue(Page, null) as string;
         }

         stop.Stop();

         Response.Write("Reflection: " + stop.ElapsedMilliseconds.ToString());
     }

     void Dynamic()
     {
         Stopwatch stop = new Stopwatch();
         stop.Start();

         dynamic page = Page;
         for (int i = 0; i < reps; i++)
         {
             string url = page.Title; //Request.Url.AbsolutePath;    
         }

         stop.Stop();

         Response.Write("Dynamic: " + stop.ElapsedMilliseconds.ToString());
     }

The dynamic code runs in 4-5 milliseconds while the Reflection code runs around 200+ milliseconds! There’s a bit of overhead in the first dynamic object call but subsequent calls are blazing fast and performance is actually much better than manual Reflection.

Dynamic is definitely a huge win-win situation when you need dynamic access to objects at runtime.

© Rick Strahl, West Wind Technologies, 2005-2010
Posted in .NET  CSharp  
kick it on DotNetKicks.com

© West-Wind or respective owner

Related posts about .NET

Related posts about CSharp