这是动态的错误吗?
在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)”无法访问
不可访问的原因是类型参数T
是Dispatcher
无法访问的私有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); }