【问题标题】:Is this a bug in dynamic?这是动态的错误吗?
【发布时间】:2011-05-30 10:13:48
【问题描述】:

当在泛型类上使用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)' 由于其保护级别而无法访问

无法访问的原因是类型参数TDispatcher&lt;T&gt;无法访问的私有CallType。因此,CallDispatch 必须是不可访问的 - 但事实并非如此,因为它可以作为 T 访问。

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

【问题讨论】:

  • 鉴于这是一个部分类,问题可能出在其他地方(因为显然这不是唯一的文件)。
  • Alxandr:这是唯一的文件。部分存在是因为我之前将它用作部分类并且忘记删除它。
  • 我不认为这是一个错误。我推测出于您的代码所展示的确切原因,事情被设为私有 - 不应该在不应该的地方被调用。
  • 我在 Connect 上打开了一个查询:connect.microsoft.com/VisualStudio/feedback/details/672411/…

标签: c# dynamic compiler-bug


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

【讨论】:

  • @Nathan:如果Dispatcher&lt;CallType&gt; 没有足够的访问权限来拨打电话,我的版本将会失败。但是我的代码编译并运行没有错误。因此,所需的一切都可以访问,而 DLR 错误地提出错误说明。
  • Nathan:不,我的问题是:“为什么我不能动态调用这个调用,而我可以静态调用它?这是一个错误,还是我错过了什么?”
  • 并非每个静态有效的调用都是动态有效的。例如:IList&lt;int&gt; x = new int[10]; Console.WriteLine(x.Count); 工作正常 - 但如果您使用 dynamic x = new int[10]; Console.WriteLine(x.Count); 则不会,因为 Count 已明确实现。
  • @Ben:我的观点是,除了“如果它静态工作,它应该动态工作”之外,还有一些微妙之处。不要忘记此时它没有将它用作泛型方法(它不再将其视为 T),因此它试图调用返回类型不可访问的方法。我不知道我是否会将其归类为错误或只是另一个动态陷阱。 “应该”工作的确切性质是非常不明确的。
  • @Ben:Doh,你是对的。这不是我第一次在这个例子中被绊倒......
【解决方案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 以及编译器在调用中包含的静态信息的问题。它可以与手动设置 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-11-02
    • 2015-07-21
    • 2017-06-04
    • 1970-01-01
    • 2011-10-31
    • 2012-03-18
    • 2015-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多