Search code examples
c#dynamiccompiler-bug

Is this a bug in dynamic?


When implementing dynamic dispatch using dynamic on a generic class, and the generic type parameter is a private inner class on another class, the runtime binder throws an exception.

For example:

using System;

public abstract class Dispatcher<T> {
    public T Call(object foo) { return CallDispatch((dynamic)foo); }

    protected abstract T CallDispatch(int foo);
    protected abstract T CallDispatch(string foo);
}

public class Program {
    public static void Main() {
        TypeFinder d = new TypeFinder();

        Console.WriteLine(d.Call(0));
        Console.WriteLine(d.Call(""));
    }

    private class TypeFinder : Dispatcher<CallType> {
        protected override CallType CallDispatch(int foo) {
            return CallType.Int;
        }

        protected override CallType CallDispatch(string foo) {
            return CallType.String;
        }
    }

    private enum CallType { Int, String }
}

Here, a RuntimeBinderException will be thrown with the message

'Dispatcher.CallDispatch(int)' is inaccessible due to its protection level

The reason for the inaccessibility is that the type parameter T is the private CallType which Dispatcher<T> cannot access. Therefore, CallDispatch must be inaccessible - but it isn't, because it's accessible as T.

Is this a bug with dynamic, or is this not supposed to be supported?


Solution

  • It's a bug. If you can make the call statically (and you can), you should be able to make it dynamically.

    Specifically, the following code works:

    using System;
    
    public abstract class Dispatcher<T> {
        public T Call(object foo)
        {
            return CallDispatch(((object)(dynamic)foo).ToString());
        }
    
        protected abstract T CallDispatch(int foo);
        protected abstract T CallDispatch(string foo);
    }
    
    public class Program {
        public static void Main() {
            TypeFinder d = new TypeFinder();
    
            Console.WriteLine(d.Call(0));
            Console.WriteLine(d.Call(""));
        }
    
        private class TypeFinder : Dispatcher<CallType> {
            protected override CallType CallDispatch(int foo) {
                return CallType.Int;
            }
    
            protected override CallType CallDispatch(string foo) {
                return CallType.String;
            }
        }
    
        private enum CallType { Int, String }
    }
    

    Note that I've used ToString() to make the static type known, the C# compiler and CLR allow this context to access the private type CallType, so the DLR should allow it as well.