如何标记Eric Lippert?
如果您有想让我注意的事情,可以使用我博客上的"联系"链接。
我仍然完全不明白为什么C#编译器会在毫无意义的命名空间和类型碰撞的情况下检查它们之间的冲突。
确实,在这里的规则很棘手。巧合的是,两周前我写了一系列关于与此问题密切相关的问题的博客文章;它们将在三月初发布。请关注博客获取详情。
更新:上述文章在此处:
链接
为什么会出现这个错误?
让我用几个问题来重新表述这个问题。
哪些规范部分证明了出现此错误的产生?
我认为其他答案已经对此进行了令人满意的解释。类型解析算法非常规范化。但总结一下:在正确名称的东西“内部”时比从“外部”使用正确名称的东西“更紧密地绑定”。当您说:
using XYZ;
namespace ABC.DEF
{
class GHI : DEF { }
}
那与
using XYZ;
namespace ABC
{
namespace DEF
{
class GHI : DEF { }
}
}
现在我们必须确定DEF的含义。我们从内部向外部看。是否有名为DEF的GHI类型参数?没有。看一下容器。是否有名为DEF的DEF成员?没有。看一下容器。是否有名为DEF的ABC成员?
是。我们完成了;我们已经确定了DEF的含义,它是一个命名空间。我们在询问“XYZ是否有一个名为DEF的成员?”之前发现了DEF的含义。
哪些设计原则影响了这个设计?
一个设计原则是“名称无论如何使用都表示相同的含义”。语言并不完全遵守这个原则;在同一代码中,同一个名称可能用于指代两个不同的事物。但通常情况下,我们努力使得在同一上下文中看到两次“Foo”时,它表示相同的含义。(请参见
The Color Color Problem和
identifying violations of the "simple name" rules中的一些详细信息。)
另一个设计原则是“不回溯”。在C#中,我们永远不会说“我看到你使用了一个名称来引用在这个上下文中不能引用的东西。让我放弃名称绑定的结果,重新开始寻找可能有效的东西。”
一个更大的原则支撑着“不回溯”原则,即C#不是一种“猜测用户意图”的语言。您编写了一个程序,在这个程序中,最佳的标识符绑定是一个命名空间,但实际上应该是一个类型。有两种可能性。第一种可能性:您犯了一个错误,希望得到告知以便
您采取措施进行纠正。第二种可能性:您希望我们选择一个较差的绑定,因此我们应该从所有可能的较差绑定中
猜测出您可能想要的那个。
在像JScript这样的语言中,这是一个很好的设计原则——JScript是关于当开发者做一些疯狂的事情时混淆的。但C#不是这种类型的语言;我们从开发者那里清楚地获得的反馈是:
告诉我什么地方出了问题,以便我可以修复它。
“不回溯”的事情在于它使语言更容易理解。假设您有这样的东西:
namespace XYZ.DEF
{
public class GHI {}
}
namespace QRS.DEF.GHI
{
public class JKL { }
}
...
using QRS;
namespace TUV
{
using XYZ;
namespace ABC
{
namespace DEF
{
class GHI { }
class MNO : DEF.GHI.JKL { }
}
}
}
计算MNO的基本类型。不使用回溯,我们说“DEF是ABC.DEF”,因此GHI是ABC.DEF.GHI。因此,JKL是ABC.DEF.GHI.JKL,但这种类型不存在,会出现错误。您必须通过提供一种类型名称来修复该错误,以便编译器可以识别您想要的DEF。
如果我们使用回溯,我们需要做什么?我们得到了那个错误,然后我们回溯。XYZ包含DEF吗?是的。它包含GHI吗?是的。它包含JKL吗?不是。再次回溯。QRS包含DEF.GHI.JKL吗?是的。
这样做是可行的,但我们能从它的可行性中逻辑地推断出它就是用户想要的吗?
在这种疯狂的情况下,谁知道呢?我们在其中得到了各种良好的绑定,然后在游戏的最后阶段出现问题。我们偶然发现所需答案的想法似乎非常可疑。
在这里正确的做法不是多次回溯并尝试所有阶段的更糟绑定。正确的做法是说:“伙计,这个查找的最佳匹配结果给出了荒谬的结果;请给我一些不太含糊的东西来处理。”
很不幸,编写一种语言的事实是,如果最佳匹配不起作用,编译器会大声抱怨,这就意味着开发人员经常说“当然,在一般情况下,我希望编译器指出我的所有错误——或者更确切地说,我的所有同事的错误。但对于这个特定的案例,我知道我在做什么,所以请编译器做我想要的而不是我说的。”问题在于,你不能两全其美。你不能同时拥有一个强制执行严格规则的编译器,使可疑代码被积极地识别为错误,同时允许通过编译器启发式来编写疯狂的代码,以便找出“我实际上想表达的意思”,当你写一些编译器合理看作是有歧义或错误的东西时。如果您想了解许多专业开发人员如何强烈反感一种语言设计的影响,该设计积极识别错误而不是猜测开发人员希望选择更糟糕的结果,请参见
此文章上的116条评论,关于重载分辨率的次要和相当不重要的方面:(请注意,我不再回答有关此问题的评论;我已经解释了我的立场十多次。如果所有这些解释都无法令人信服,那是因为我不是一个很好的说服者。)
最后,如果你真的想测试自己对C#名称解析规则的理解,
尝试这个小谜题。几乎每个人都会做错,或者因为错误的原因而得到正确答案。答案在
这里。