Подтвердить что ты не робот

Это ошибка в динамике?

При реализации динамической рассылки с использованием dynamic в общем классе, а параметр generic type является частным внутренним классом в другом классе, связующее средство времени выполнения генерирует исключение.

Например:

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 }
}

Здесь RuntimeBinderException будет выведено с сообщением

'Dispatcher.CallDispatch(int)' недоступен из-за уровня защиты

Причиной недоступности является то, что параметр типа T является закрытым CallType, к которому Dispatcher<T> не может получить доступ. Поэтому CallDispatch должен быть недоступен - но это не так, потому что он доступен как T.

Является ли это ошибкой с dynamic, или это не должно поддерживаться?

4b9b3361

Ответ 1

Это ошибка. Если вы можете сделать вызов статически (и вы можете), вы сможете сделать его динамически.

В частности, работает следующий код:

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 }
}

Обратите внимание, что я использовал ToString(), чтобы сделать статический тип известным, компилятор С# и CLR разрешают этому контексту доступ к закрытому типу CallType, поэтому DLR также должна его разрешать.

Ответ 2

Это ошибка, потому что следующее статическое изменение набора должно быть эквивалентным

using System;

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

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

И он работает.

Эта проблема, по-видимому, является проблемой с компилятором и вызовами dlr, которые он делает, и статической информацией, которую компилятор включает в вызов. Его можно обойти с открытым исходным кодом ImpromptuInterface, который вручную настраивает вызовы dlr. С помощью Impromptu, установив контекст в this, он получает разрешения доступа из типа среды выполнения, который будет TypeFinder.

using System;
using ImpromptuInterface.Dynamic;
public abstract class Dispatcher<T>
{
    protected CacheableInvocation _cachedDynamicInvoke;

    protected Dispatcher()
    {
        _cachedDynamicInvoke= new CacheableInvocation(InvocationKind.InvokeMember, "CallDispatch", argCount: 1, context: this);
    }

    public T Call(object foo)
    {
        return (T) _cachedDynamicInvoke.Invoke(this, foo);
    }

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