我只是遇到了最奇怪的事情,现在我有点头脑=吹了 ...
以下程序可以正常编译,但是在运行该程序时,尝试读取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;
该程序将失败,因为无法访问
Empty
。 dynamic
不允许您进行首先是非法的分析。但是,运行时分析器意识到了这一点,并决定作弊一点。它问自己“是否有可访问的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/