我只是遇到了最奇怪的事情,现在我有点头脑=吹了 ...

以下程序可以正常编译,但是在运行该程序时,尝试读取RuntimeBinderException时会得到Value'object' does not contain a definition for 'Value'

class Program
{
    interface IContainer
    {
        int Value { get; }
    }

    class Factory
    {
        class Empty : IContainer
        {
            public int Value
            {
                get { return 0; }
            }
        }

        static IContainer nullObj = new Empty();

        public IContainer GetContainer()
        {
            return nullObj;
        }
    }

    static void Main(string[] args)
    {
        dynamic factory = new Factory();
        dynamic container = factory.GetContainer();
        var num0 = container.Value; // WTF!? RuntimeBinderException, really?
    }
}

这是令人振奋的部分。将嵌套的Factory+Empty类型移到Factory类之外,如下所示:
class Empty : IContainer
{
    public int Value
    {
        get { return 0; }
    }
}

class Factory...

程序运行正常,有人在乎解释为什么会这样吗?

编辑

当然,在编码冒险中,我做了一些我应该首先考虑的事情。这就是为什么您看到我对私有(private)类和内部类之间的差异进行了一些讨论。这是因为我设置了InternalsVisibleToAttribute,该代码使我的测试项目(在此实例中使用的是位)的行为与它们的行为相同,这都是设计使然,尽管从一开始就暗示了我的意思。

阅读埃里克·利珀特(Eric Lippert)的答案,对其余内容进行很好的解释。

使我真正警惕的是,动态绑定(bind)器考虑了实例类型的可见性。我有很多JavaScript经验,并且作为一个JavaScript程序员,实际上没有公开或私有(private)的东西,我完全被可见性很重要这一事实所愚弄,我的意思是说,毕竟,我就像访问该成员一样它是公共(public)接口(interface)类型的(我认为动态只是反射的语法糖),但是动态绑定(bind)器无法做出这样的假设,除非您使用简单的强制转换给它一个提示。

最佳答案

C#中“动态”的基本原理是:在运行时对表达式进行类型分析,就好像运行时类型已经是编译时类型一样。因此,让我们看看如果我们真的这样做会发生什么:

    dynamic num0 = ((Program.Factory.Empty)container).Value;

该程序将失败,因为无法访问Emptydynamic不允许您进行首先是非法的分析。

但是,运行时分析器意识到了这一点,并决定作弊一点。它问自己“是否有可访问的Empty基类?”答案显然是肯定的。因此,它决定退回基类并进行分析:
    dynamic num0 = ((System.Object)container).Value;

失败的原因是该程序会给您一个“对象没有名为Value的成员”的错误。这是您得到的错误。

动态分析永远不会说“哦,你一定要说”
    dynamic num0 = ((Program.IContainer)container).Value;

因为如果那是您的意思,那当然是您首先要编写的内容。再次,dynamic的目的是回答以下问题:如果编译器知道运行时类型,并且转换为接口(interface)不会提供您的运行时类型,那将会发生什么。

当您将Empty移到外面时,动态运行时分析器会假装您编写了以下代码:
    dynamic num0 = ((Empty)container).Value;

现在Empty可以访问并且强制转换是合法的,因此您可以得到预期的结果。

更新:



我无法重现所描述的行为。让我们尝试一个例子:
public class Factory
{
    public static Thing Create()
    {
        return new InternalThing();
    }
}
public abstract class Thing {}
internal class InternalThing : Thing
{
    public int Value {get; set;}
}
> csc /t:library bar.cs
class P
{
    static void Main ()
    {
        System.Console.WriteLine(((dynamic)(Factory.Create())).Value);
    }
}
> csc foo.cs /r:bar.dll
> foo
Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
'Thing' does not contain a definition for 'Value'

您会看到它是如何工作的:运行时绑定(bind)程序检测到InternalThing在外部程序集内部,因此在foo.exe中不可访问。因此,它属于公共(public)基本类型Thing,可以访问但没有必需的属性。

我无法重现您描述的行为,如果可以重现,则说明您发现了一个错误。如果您对该错误有少量的理解,我很乐意将其传递给我以前的同事。

关于c# - C#动态类型陷阱,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39709977/

10-12 12:44
查看更多