12

当在泛型类上实现动态调度dynamic,并且泛型类型参数是另一个类的私有内部类时,运行时绑定器会抛出异常。

例如:

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的私有参数。因此,必须是不可访问的 - 但它不是,因为它可以作为.CallTypeDispatcher<T>CallDispatchT

这是一个错误dynamic,还是不应该支持?

4

2 回答 2

4

这是一个错误。如果您可以静态地进行调用(并且可以),那么您应该能够动态地进行调用。

具体来说,以下代码有效:

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()让静态类型为人所知,C# 编译器和 CLR 允许此上下文访问私有类型CallType,因此 DLR 也应该允许它。

于 2011-05-31T12:51:43.580 回答
1

这是一个错误,因为以下静态类型更改应该是等效的

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 以及编译器在调用中包含的静态信息。它可以与手动设置 dlr 调用的开源框架ImpromptuInterface一起工作。使用 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);
}
于 2011-06-02T15:16:19.283 回答