Search code examples
c#dynamicreflection

Is the use of dynamic considered a bad practice?


In C#, someone can do:

MyClass myInstance        = new MyClass();
dynamic mydynamicInstance = myInstance;

And then, invoke a method, like:

//This method takes a MyClass argument and does something.
Caller.InvokeMethod(myDynamicInstance);

Now, this will lead to determination of the myInstance type at runtime, and, if it is valid, the Caller.InvokeMethod will be called normally.

Now, my question is if this is considered a bad practice to use dynamic, especially in the following cases:

1) InvokeMethod instantiates another instance of myDynamicInstance type, using reflection inside.

2) There is an abstract base class MyBaseClass and a number of subclasses of it, including MyBaseClass. If we have a number of overloaded methods of InvokeMethod for all of those derived classes, could we use it in order to allow at runtime the type determination and then the proper invocation via method overloading (or late binding on the call of a method of that class)?:

public abstract class MyBaseClass         {/*...*/}
public class MyClass        : MyBaseClass {/*...*/}
public class MyAnotherClass : MyBaseClass {/*...*/}

MyBaseClass myBaseClassRef = new MyClass();
dynamic myDynamicInstance  = myBaseClassRef;

Caller.InvokeMethod(myDynamicInstance);

Solution

  • The short answer is YES, it is a bad practice to use dynamic.

    Why?

    dynamic keyword refers to type late binding, which means the system will check type only during execution instead of during compilation. It will then mean that user, instead of programmer, is left to discover the potential error. The error could be a MissingMethodException, but it could be also a not intended call to an existing method with a bad behavior. Imagine a call to a method that ends in computing a bad price or in computing a bad level of oxygen.

    Generally speaking, type checking helps to get deterministic computing, and so, when you can, you should use it. Here's a question on shortcomings of dynamic.

    However, dynamic can be useful...

    • Interop with COM like with Office
    • Interop with languages where dynamic types are part of the language (IronPython, IronRuby) as dynamic was introduced to help porting them to .Net.
    • Can replace reflection complex code with low ceremony, elegant code (however depending on the case, you still should profile both approaches to check which one is the most appropriate in terms of performance and compile-time checks).

    Code base is evolving throughout the application life cycle and even if dynamic seems ok now, it set a precedent which can implies an increase of dynamic keyword usage by your team. It can lead to increased maintenance costs (in case the above stated signature evolves, you can notice it too late). Of course, you could rely on unit tests, non regression human tests and so on. But when you have to choose between human discipline related quality and automatically checked by computer related quality, choose the later. It's less error prone.

    In your case...

    In your case, it seems you can use the common inheritance scheme (the first one below and the one you mention in your question), as dynamic won't give you any additional benefit (it will just cost you more processing power and make you incurring the risk of future potential bugs).

    It depends on whether you can change code of MyClass hierarchy and/or Caller.InvokeMethod.

    Let's enumerate

    the different possible alternatives to dynamic...

    • Compiled type-checked alternative to dynamic keyword method call:

    The most common is using interface virtual call like this instance.InvokeMethod() with inheritance calling the right implementation.

    public interface IInvoker : { void InvokeMethod(); }
    public abstract class MyBaseClass : IInvoker { public abstract void InvokeMethod(); }
    public class MyAnotherClass : MyBaseClass { public override void InvokeMethod() { /* Do something */ } }
    public class MyClass : MyBaseClass { public override void InvokeMethod() { /* Do something */ } }
    

    Another a little less performant is by using Extension Methods

    public static class InvokerEx:
    {
        public static void Invoke(this MyAnotherClass c) { /* Do something */ } }
        public static void Invoke(this MyClass c) { /* Do something */ } }
    }
    

    If there are several "visitors" of MyBaseClass hierarchy, you can use the Visitor pattern:

    public interface IVisitor 
    {
        void Visit(this MyAnotherClass c);
        void Visit(this MyClass c);
    }
    
    public abstract class MyBaseClass : IInvoker { public abstract void Accept(IVisitor visitor); }
    public class MyAnotherClass : MyBaseClass { public override void Accept(IVisitor visitor) { visitor.Visit(this); } }
    public class MyClass : MyBaseClass { public override void Accept(IVisitor visitor) { visitor.Visit(this); } }
    

    Other variants though not very useful here (Generic method) but interesting for the performance comparison:

    public void InvokeMethod<T>(T instance) where T : IInvoker { return instance.InvokeMethod(); }
    
    • Dynamic alternative to dynamic keyword method call :

    If you need to call a method not known at compile time, I've added below the different techniques you could use and updated the performance results:

    MethodInfo.CreateDelegate

            _method = typeof (T).GetMethod("InvokeMethod");
            _func = (Func<T, int>)_method.CreateDelegate(typeof(Func<T, int>));
    

    Note: Cast to Func is needed to avoid call DynamicInvoke (as it is generally slower).

    DynamicMethod and ILGenerator.Emit

    It actually build the full call from scratch, it's the most flexible but you must have some assembler background to fully appreciate it.

            _dynamicMethod = new DynamicMethod("InvokeMethod", typeof (int), new []{typeof(T)}, GetType().Module);
            ILGenerator il = _dynamicMethod.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Call, _method);
            il.Emit(OpCodes.Ret);
            _func = (Func<T, int>) _dynamicMethod.CreateDelegate(typeof (Func<T, int>));
    

    Linq Expression

    It's similar to DynamicMethod, however you don't control the IL generated. Though, it's really more readable.

            _method = typeof (T).GetMethod("InvokeMethod");
            var instanceParameter = Expression.Parameter(typeof (T), "instance");
            var call = Expression.Call(instanceParameter, _method);
            _delegate = Expression.Lambda<Func<T, int>>(call, instanceParameter).Compile();
            _func = (Func<T, int>) _delegate;
    

    MethodInfo.Invoke

    The last but not the least, the standard known reflection call. However, even if it's easy to mess with it, don't use it as it's really a bad performer (look at the benchmark results). Prefer CreateDelegate which is really faster.

            _method = typeof (T).GetMethod("InvokeMethod");
            return (int)_method.Invoke(instance, _emptyParameters);
    

    Code of the benchmark test can be found on GitHub.

    Benchmark of the different methods to get an order of magnitude (for 10 Millions of call) (.NET Framework 4.5):

    Method Elapsed Calls per ms Scale
    Class standard call 00:00:00.0532945 188679 1
    Extension Method 00:00:00.0637817 158730 1.19
    Generic Method 00:00:00.0772658 129870 1.45
    Interface virtual 00:00:00.0778103 129870 1.45
    MethodInfo.CreateDelegate 00:00:00.1131495 88495 2.13
    DynamicMethod.Emit 00:00:00.1152792 86956 2.17
    Visitor Accept/Visit 00:00:00.1384779 72463 2.60
    Linq Expression 00:00:00.3158967 31746 5.94
    Keyword dynamic 00:00:00.3805229 26315 7.17
    MethodInfo.Invoke 00:00:05.3104416 1883 100.19

    EDIT:

    So comparing to Visitor pattern, dynamic dispatch is just about 3 times slower. It can be acceptable for some applications as it can remove cumbersome code. It's always up to you to choose.

    Just keep in mind all the drawbacks.


    EDIT: (as an answer to multiple dispatch benefit)

    Using trendy pattern name like 'multiple dispatch' and just state that it's cleaner because it uses less code, doesn't make it an added benefit IMHO. If you want to write trendy code or don't care about type safety and production stability, there are already a lot of language out there offering full feature dynamic typing. I see dynamic keyword introduction in C# as a way to close the gap between the strong typed language family and not so strongly typed other languages. It doesn't mean you should change the way you develop and put type checks to trash.

    UPDATE: 2016/11/08 (.NET Framework 4.6.1)

    Orders of magnitude remain the same (even if some of them have improved a bit):

    Class standard call: 1
    Extension Method call : 1,19
    Interface virtual call : 1,46
    Generic Method call : 1,54
    DynamicMethod.Emit call : 2,07
    MethodInfo.CreateDelegate call : 2,13
    Visitor Accept/Visit call : 2,64
    Linq Expression call : 5,55
    Keyword dynamic call : 6,70
    MethodInfo Invoke call : 102,96