11 Aug
2011

Dynamic in C# is Broken

Category:UncategorizedTag: :

Earlier this week, I ran into an issue while using the dynamic keyword in C#. I learned from C# in Depth that there are a couple of restrictions with dynamic, most notably when using extension methods or converting lambda expressions. But apparently there are more restrictions than meets the eye, which came as an unpleasant surprise. Let?s look at some code.

public interface IPresenter
{
    void Start(dynamic startupData);
}

public interface ISpecificPresenter : IPresenter
{}

public class SpecificPresenter : ISpecificPresenter
{
    public void Start(dynamic startupData)
    {
        // ...
    }
}

Here we have an interface called IPresenter with a single method on it named Start. Notice that the Start method has a single parameter which is defined as dynamic. We also have another interface called ISpecificPresenter that inherits from IPresenter. Finally we have a concrete class that implements the ISpecificPresenter interface. So far, so good.

Next we have a class called Activator that we use to kick-off a particular presenter.

public class Activator
{
    public void Start<TPresenter>(dynamic startupData)
        where TPresenter : IPresenter
    {
        var presenter = ObjectFactory.GetInstance<TPresenter>();
        presenter.Start(startupData);
    }
}

The Activator class has a generic Start method with a parameter named startupData that is also dynamic. Here we simply get an instance of a requested presenter from StructureMap and call its Start method. This is how to call the Start method of the Activator class :

var activator = new Activator();
activator.Start<ISpecificPresenter>(new{});

When we run this code, we get the following exception:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: ‘ISpecificPresenter’ does not contain a definition for ‘Start’.

This had me stumped for a while until I added a cast from ISpecificPresenter to IPresenter in the Start method of the Activator class.

public class Activator
{
    public void Start<TPresenter>(dynamic startupData)
        where TPresenter : IPresenter
    {
        var presenter = (IPresenter)ObjectFactory.GetInstance<TPresenter>();
        presenter.Start(startupData);
    }
}

Now everything works as expected. But I?m not entirely sure why this isn?t behaving as I expected. I guess it has something to do with the fact that the Start method is defined on the IPresenter interface and not on the ISpecificPresenter interface.

image

Although I very much like the flexibility that dynamic objects bring to the C# language, I?m also starting to think that it might not be such a good idea to bring dynamic concepts to a statically typed language. Running into these kind of issues and limitations reinforces this growing opinion.

I would much appreciate it if anyone could enlighten me with a good explanation.

Until next time.

17 thoughts on “Dynamic in C# is Broken

  1. Are you sure this is related to dynamic? Do you get the same behavior when not having a dynamic parameter? Do you get the same behavior when not using the ObjectFactory? What type of object exactly does ObjectFactory return?

  2. When you change the signature to object then you have the same behavior. So i don’t think its related to the dynamic object.

  3. Jan,
    couldn’t it be that an interface requires your object to implement the entire
    hierarchy but the interface itself does not know about its parents?  

    So your object knows its supposed to implement
    the entire tree, but when you assign the reference to an interface you only
    have access to that interface’s methods.

  4. It is related to the dynamic parameter of Activator.Start. If you change that to object, it works. This code snippet demonstrates the problem:

        public interface IBase {
            void M(object p);
        }
        
        public interface IDerived : IBase {}

        public class MyClass : IDerived { 
            public void M(object p) {
                Console.WriteLine(“ok”);
            }
        }

        public class Program {
            public static void Main() {
               // change to object to make it work
               dynamic p = new {};
                IDerived sut = new MyClass();
                sut.M(p);
            }
        }

     

  5. Of course, these bring up a different point : Under what circumstances would you need to pass a dynamic TO a method?  I can see where you might need to return a dynamic from a method, but if the called method is going to make use of it’s given parameters, it’s going to have to know specifically what that are.

  6. The problem is that the presenter inside Activator.Start is of type TPresenter, which is a generic type without any constraints, which makes it behave as Object. Thus, you can’t call Start on it. If you declare presenter as dynamic, it should work.

  7. That’s looks like a bug in the compiler. The inferred type of presenter is ISpecificPresenter, but it seems like the compiler is making a mistake when generating the dynamic call.

  8. Can you tell me what the difference in my code and yours is (other than I use Activator to create the instance of a class, since I am not sure what your ObjectFactory is doing).

    My code works as expected no exceptions etc.

    using System;

    namespace Dynamic
    {
        public interface IPresenter
        {
            void Start(dynamic data);
        }
        public interface ISpecificPresenter : IPresenter
        {
            void End();
        }
        public class SpecificPresenter : ISpecificPresenter
        {

            public void End()
            {
                Console.WriteLine(“End”);
            }

            public void Start(dynamic data)
            {
                Console.WriteLine(“Start {0}”, data.ToString());
            }
        }
        public class MyActivator
        {
            public void Start(dynamic data) where T : IPresenter
            {
                var presenter = Activator.CreateInstance();
                presenter.Start(data);
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                var activator = new MyActivator();
                activator.Start(new { });
                activator.Start(“foo”);
            }
        }
    }

  9. In this case I’m passing all kinds of anonymous objects with different  properties (kinda like passing JSON objects objects around in JavaScript). Having a dynamic parameter eases things as I can write something like 

     Start(new { Prop1 = “1”, Prop2 = “2” }.ToExpando());

  10. You could use a simple factory method as well for emulating StructureMap:

    This way you don’t have to use Activator.CreateInstance:

            private static ISpecificPresenter CreateSpecificPresenter()        {            return new SpecificPresenter();        }

  11. I don’t know, reading this I can’t help but think of Jamie Zawinski’s quote:
    Some people, when confronted with a problem, think “I know, I’ll use regular expressions.” Now they have two problems.

    I’m not a fan of dynamic, it smells of “variant” from VB days. (Yes, I know the differences, hence “smells of” not “is”) Like regular expressions, using dynamic like that with anonymous objects as parameters will seem like a clever solution today (much as writing a good, powerful Regex) but down the road it will likely lead to pain and suffering as behavioural changes are needed. (How do you regression test something that accepts absolutely anything? and how do future developers, or even yourself understand those beasts 6-12 months after the fact?)

  12. You might be right. As I mentioned, I’m still in doubt whether dynamic is a good language feature for C#. Why not going all the way then by embracing a full dynamic language.

    On the other hand, future developers just have to learn to read the unit tests that I have in place. Dynamic code without unit tests is indeed a beast.

  13. I’ve found that it has its uses, particularly when working with com objects, and also if you’re working with something that’s inherently not typesafe e.g. reading from an Excel document via ODBC etc.. It’s definitely not a silver bullet to solving all coding issues tho 🙂

Comments are closed.