这是动态的错误吗?

在generics类上使用dynamic实现动态分派,并且generics类型参数是另一个类上的私有内部类时,运行时绑定程序会引发exception。

例如:

 using System; public abstract class Dispatcher { 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 { 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)”无法访问

不可访问的原因是类型参数TDispatcher无法访问的私有CallType 。 因此, CallDispatch必须是不可访问的 – 但它不是,因为它可以作为T访问。

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

这是一个错误。 如果您可以静态拨打电话(并且可以),您应该能够动态地进行呼叫。

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

 using System; public abstract class Dispatcher { 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 { 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也应该允许它。

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

 using System; public abstract class Dispatcher { 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通过设置上下文,它将从运行时类型获取访问权限,这将是TypeFinder。

 using System; using ImpromptuInterface.Dynamic; public abstract class Dispatcher { 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); }