Partilhar via


Using C# dynamic to call static members

By now, you’ve probably heard that C# 4.0 is adding support for the dynamic keyword, which introduces some aspects of dynamic languages to C#.  I had not had a chance to really try it, but recently I was reading Bertrand Le Roy’s post on the topic, and was sort of looking for a good opportunity to use it.

Today, I found a scenario which I thought it would work great for, but it turned out not to be supported out of the box!

The scenario is to call static class members using dynamic.  That probably sounds crazy, so let’s look at an example.  Say you have these two classes:

 public class Foo1 {
    public static string TransformString(string s) { return s.ToLower(); }
    public static string MyConstant { get { return "Constant from Foo1"; } }
}

public class Foo2 {
    public static string TransformString(string s) { return s.ToUpper(); }
    public static string MyConstant { get { return "Constant from Foo2"; } }
}

Note that they are unrelated classes, but share some members with the same signature.  In a sense, you could say that they two classes share a duck type signature.

Now here is the problem we’re trying to solve: given a System.Type object of either class (or any other random class that shares those members), how can you call those members?   Concretely, we’re trying to implement this method:

 static void MakeTestCalls(Type type) {
    // Call TransformString("Hello World") on this type

    // Get the MyConstant property on this type
}

How can we implement this method? Ok, we can do it the old fashion way using reflection, e.g.

 static void MakeTestCalls(Type type) {
    Console.WriteLine(type.GetMethod("TransformString").Invoke(null, new object[] { "Hello World" }));
    Console.WriteLine(type.GetProperty("MyConstant").GetValue(null, null));
}

That works, but it’s ugly.  These are the very type of things that dynamic is supposed to improve.  So my first naive attempt was to do this:

 static void MakeTestCalls(Type type) {
    dynamic fooTypeDynamic = type;

    Console.WriteLine(fooTypeDynamic.TransformString("Hello World"));
    Console.WriteLine(fooTypeDynamic.MyConstant);
}

Basically, the theory was that when assigning a System.Type to a dynamic variable, it would let you call static members from it.  I didn’t really expect it to work, but I at least had to try! :)  And sure enough, it didn’t work, blowing up with: “Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'System.RuntimeType' does not contain a definition for 'TransformString'”.

So I then posted the question on our internal C# list, and got a reply from C# guru Eric Lippert, basically saying that it was a potentially interesting idea but was just not supported in C# 4.0.  Fair enough, this is only the beginning of C# dynamic, and it doesn’t do everything.

But I then went back to Bertrand’s post where he gives a great sample of how you can teach dymamic new tricks by implementing a custom DynamicObject.  And it turned out to be relative simple.  Here is the class I ended up with:

 public class StaticMembersDynamicWrapper : DynamicObject {
    private Type _type;
    public StaticMembersDynamicWrapper(Type type) { _type = type; }

    // Handle static properties
    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        PropertyInfo prop = _type.GetProperty(binder.Name, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public);
        if (prop == null) {
            result = null;
            return false;
        }

        result = prop.GetValue(null, null);
        return true;
    }

    // Handle static methods
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
        MethodInfo method = _type.GetMethod(binder.Name, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public);
        if (method == null) {
            result = null;
            return false;
        }

        result = method.Invoke(null, args);
        return true;
    }
}

The idea is pretty simple: when the runtime needs to call something, it basically asks you to do it.  It passes you the method (or property) name and the parameters, and you take it from there. And in this case, of course, we’re using reflection.

Once we have that, the real fun starts as we’re now able to call our static members using dynamic!

 static void MakeTestCalls(Type type) {
    dynamic typeDynamic = new StaticMembersDynamicWrapper(type);

    // Call TransformString("Hello World") on this type
    Console.WriteLine(typeDynamic.TransformString("Hello World"));

    // Get the MyConstant property on this type
    Console.WriteLine(typeDynamic.MyConstant);
}

Note how we wrap the type into our custom DynamicObject is order to have the dynamic invocations go through us.

Now you might say that the scenario above where you have two classes with identical static members doesn’t seem like something that would actually occur commonly in real apps.  But once you start bringing in generics, it can actually be more common.  e.g. suppose you have something like:

 public class Table<T> {
    public static IEnumerable<T> Records { get { [return records from database table T] } }
}

The idea is that the class is an abstraction for a database table.  So Table<Product>.Records returns an IEnumerable<Products>, and Table<Category>.Records returns an IEnumerable<Category>.  Now suppose you’re writing some table agnostic code that can work with the data from any table.  You have a System.Type for some Table<T>, and you need to get its Records property.  Even though it seems like it’s the same Records property coming for a base class, the reality is that it’s a completely different property for each T, and C# provides no simple way of making the call.  But with the technique above, you get to access the property with a much simpler syntax than with reflection.

The zipped sample is attached to this post.

DynamicCallsToStaticMembers.zip

Comments

  • Anonymous
    October 23, 2009
    it  seems like a ordinary case to me...

  • Anonymous
    October 23, 2009
    The comment has been removed

  • Anonymous
    October 23, 2009
    O.k. tried it. No (simple) way to bind a delegate dynamically to an unknown types function without using reflections invoke - I'm still too much influenced by C++ template binding possibilites ;-) So your sample is the only way to accomplish that in C# for generic types (easily). A cool method for generic static calls, where the overhead is negligible. Although I still would prefer static bindings over dynamic ones, to catch most of the bugs at compilation time. Andre

  • Anonymous
    October 24, 2009
    @Andre: I'm with you on favoring static bindings whenever possible. But for cases where they're not easily possible, 'dynamic' offers a cleaner syntax than its alternatives.

  • Anonymous
    October 24, 2009
    I can see this being useful when you're "duct-taping" classes in 3rd party libraries... however, if you own the classes, I would prefer the use of interfaces (as both you and Andre already mentioned). Interesting read, nonetheless :) Thanks

  • Anonymous
    October 25, 2009
    Hy Thanks for this insight. Although I'm a bit skeptic about the over usage of dynamic keyword. I also prefer the usage of a strict and strongly typed domain over the dynamic approach. In my point of view the dynamic approach should only be used in rare cases because actually during design time you have no guarantee that what your are coding is semantically correct and you get the surprises when you are executing your program. Daniel

  • Anonymous
    October 25, 2009
    dynamic call, quite cool feature!

  • Anonymous
    October 26, 2009
    Coincidence, three days ago I posted a 'proposal' (nothing official here...) for static interface to resolve this kind of thing in C#. You can read it here : http://thinkbeforecoding.com/post/2009/10/21/CSharp-Static-interfaces and here http://thinkbeforecoding.com/post/2009/10/22/CSharp-Static-interfaces-Take-2

  • Anonymous
    October 26, 2009
    Hi, It's like Jayson says it's nice for "duct-taping" 3rd party libraries. It saved my day! :)

  • Anonymous
    October 26, 2009
    @Jérémie: the idea of static interfaces is potentially interesting.  One different is that in your case, you have T has a generic type param (as in IsInstanciated<T>() where T: ICountable). But how would you 'cast' to this interface if all you had was a System.Type, in a really late bound scenario?

  • Anonymous
    November 03, 2009
    Nice work, this is useful, however there are two problems with your approach:   1) no handling of overloaded methods   2) incorrect handling of optional/named parameters I can't post the code here (in the comments since it would look terrible), but I was able modify the code to check the Binder and get the Parameters for the methods of the type to correctly handle the two issues above. Thanks for the headstart!

  • Anonymous
    November 03, 2009
    @David Kujawski: indeed, my code above is just a proof of concept and doesn't deal with cases like method overloading.  Thanks for pointing this out!

  • Anonymous
    November 22, 2009
    This is a nice in that it is an easy to follow example of using the DynamicObject class, but... Even in the case where you are forced to rely on 3rd party libraries that are exposing the functionality you need as static methods, you should be able to wrap that functionality inside your own classes and then have them implement a common interface. Therefore I am skeptical about whether the above approach is actually something you would ever want to do.

  • Anonymous
    January 09, 2010
    Thanks for this.  You can take it a step further with extension methods: <code>        static readonly Dictionary<Type, StaticMembersDynamicWrapper> _typeLookup = new Dictionary<Type, StaticMembersDynamicWrapper>();        public static dynamic StaticMembersProxy(this Type type)        {            StaticMembersDynamicWrapper smdw;            if (!_typeLookup.TryGetValue(type, out smdw))                _typeLookup[type] = smdw = new StaticMembersDynamicWrapper(type);            return smdw;        } </code>

  • Anonymous
    January 09, 2010
    @Stephen: agreed, that's a nice little addition.

  • Anonymous
    September 10, 2010
    This could've been great for the various .Parse method overloads that the primitive BCL types provide (The Convert class doesn't solve everything.)

  • Anonymous
    April 21, 2011
    With some help, worked out a way to access the constant fields too: stackoverflow.com/.../accessing-constants-with-a-dynamic-keyword