C#动态类型问题
我只是遇到了最奇怪的事情,我有点心思=此刻…
下面的程序编译得很好,但是当你运行它时,当你尝试读取Value
时会得到一个RuntimeBinderException
。 '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
类之外移动嵌套类型Factory+Empty
,如下所示:
class Empty : IContainer { public int Value { get { return 0; } } } class Factory...
程序运行得很好,有人在乎解释为什么会这样吗?
编辑
在我的编码冒险中,我当然做了一些我应该首先考虑的事情。 这就是为什么你看到我在私人和内部阶级之间的差异。 这是因为我设置了InternalsVisibleToAttribute
,这使我的测试项目(在这个例子中消耗了比特)表现得像他们一样,这完全是设计的,尽管从一开始就暗指我。
阅读Eric Lippert的答案,对其余部分进行了很好的解释。
真正引起我注意的是动态绑定器考虑了实例类型的可见性。 我有很多JavaScript经验,作为一个JavaScript程序员,真的没有公共或私人这样的东西,我完全被可见性很重要的事实所迷惑,我的意思是,毕竟,我正在访问这个成员,好像它是公共接口类型(我认为动态只是用于reflection的语法糖)但动态绑定器不能做出这样的假设,除非你使用简单的强制转换给它一个提示。
C#中“动态”的基本原则是:在运行时对表达式进行类型分析, 就好像运行时类型是编译时类型一样 。 那么让我们看看如果我们真的这样做会发生什么:
dynamic num0 = ((Program.Factory.Empty)container).Value;
该程序将失败,因为无法访问Empty
。 dynamic
不允许你做一个本来就是非法的分析。
但是,运行时分析器意识到这一点,并决定作弊。 它问自己“是否有一个可以访问的基类?” 答案显然是肯定的。 所以它决定回到基类并分析:
dynamic num0 = ((System.Object)container).Value;
哪个失败是因为该程序会给你一个“对象没有名为Value的成员”错误。 你得到的错误是哪个。
动态分析永远不会说“哦,你一定有意”
dynamic num0 = ((Program.IContainer)container).Value;
因为当然, 如果这就是你的意思,那就是你本来应该写的东西 。 同样, dynamic
的目的是回答问题,如果编译器知道运行时类型会发生什么 ,并且转换为接口不会为您提供运行时类型。
当你将Empty
移到外面时,动态运行时分析器假装你写了:
dynamic num0 = ((Empty)container).Value;
现在可以访问Empty
并且演员表是合法的,因此您可以获得预期的结果。
更新:
可以将该代码编译成一个程序集,引用这个程序集,如果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'
并且您看到它是如何工作的:运行时绑定程序检测到InternalThing
是外部程序集的内部,因此在foo.exe中无法访问。 所以它回归到公共基础类型Thing
,它是可访问的,但没有必要的属性。
我无法重现您描述的行为,如果您可以重现它,那么您就发现了一个错误。 如果你有一个小错误的bug,我很乐意将它传递给我以前的同事。
我想,在运行时,容器方法调用只是在私有的Empty类中解决,这会导致代码失败。 据我所知,动态不能用于访问私人成员(或私人类的公共成员)
这应该(当然)工作:
var num0 = ((IContainer)container).Value;
这里是类Empty,它是私有的:所以你不能在声明类(工厂)之外操作Empty实例。 这就是你的代码失败的原因。
如果Empty是内部的,你可以在整个程序集中操纵它的实例(好吧,不是因为Factory是私有的),允许所有动态调用,并且你的代码可以工作。
上述就是C#学习教程:C#动态类型问题分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1043098.html