c#:为什么不使用方法签名解析这个不明确的枚举引用?
考虑以下代码:
namespace ConsoleApplication
{
using NamespaceOne;
using NamespaceTwo;
class Program
{
static void Main(string[] args)
{
// Compilation error. MyEnum is an ambiguous reference
MethodNamespace.MethodClass.Frobble(MyEnum.foo);
}
}
}
namespace MethodNamespace
{
public static class MethodClass
{
public static void Frobble(NamespaceOne.MyEnum val)
{
System.Console.WriteLine("Frobbled a " + val.ToString());
}
}
}
namespace NamespaceOne
{
public enum MyEnum
{
foo, bar, bat, baz
}
}
namespace NamespaceTwo
{
public enum MyEnum
{
foo, bar, bat, baz
}
}
编译器抱怨 MyEnum 在对 Frobble() 的调用中是一个不明确的引用。由于调用什么方法没有歧义,因此人们可能期望编译器根据方法签名解析类型引用。为什么不呢?
请注意,我并不是说编译器应该这样做。我相信有充分的理由证明事实并非如此。我只是想知道这是什么原因。
Consider the following code:
namespace ConsoleApplication
{
using NamespaceOne;
using NamespaceTwo;
class Program
{
static void Main(string[] args)
{
// Compilation error. MyEnum is an ambiguous reference
MethodNamespace.MethodClass.Frobble(MyEnum.foo);
}
}
}
namespace MethodNamespace
{
public static class MethodClass
{
public static void Frobble(NamespaceOne.MyEnum val)
{
System.Console.WriteLine("Frobbled a " + val.ToString());
}
}
}
namespace NamespaceOne
{
public enum MyEnum
{
foo, bar, bat, baz
}
}
namespace NamespaceTwo
{
public enum MyEnum
{
foo, bar, bat, baz
}
}
The compiler complains that MyEnum is an ambiguous reference in the call to Frobble(). Since there is no ambiguity in what method is being called, one might expect the compiler to resolve the type reference based on the method signature. Why doesn't it?
Please note that I'm not saying that the compiler should do this. I'm confident that there is a very good reason that it doesn't. I would simply like to know what that reason is.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
保罗是对的。在 C# 的大多数情况下,我们“从内到外”进行推理。
它对你来说是明确的,但与编译器无关。重载解析的任务是确定方法组
Frobble
是否可以解析为给定已知参数的特定方法。 如果我们无法确定参数类型是什么,那么我们甚至不会尝试进行重载解析。恰好只包含一个方法的方法组在这方面并不特殊。在重载决策成功之前,我们仍然需要有充分的论据。
在某些情况下,我们会从“由外到内”进行推理,即在对 lambda 进行类型分析时。这样做会使重载解析算法变得极其复杂,并且给编译器带来了一个需要解决的问题,在糟糕的情况下,这个问题至少是 NP-HARD。但在大多数情况下,我们希望避免这种复杂性和费用; 通过在父表达式之前分析子表达式来分析表达式,而不是相反。
更一般地说:C# 不是一种“当程序不明确时使用启发式方法来猜测程序员可能的意思”的语言。这是一种“通知开发人员他们的程序不清楚并且可能损坏”的语言。语言中旨在尝试解决不明确情况的部分(例如重载解析或方法类型推断或隐式类型数组)经过精心设计,以便算法具有明确的规则,将版本控制和其他现实世界的方面考虑在内。一旦程序的某个部分不明确,就立即退出是我们实现这一设计目标的一种方法。
如果您更喜欢一种更“宽容”的语言来尝试弄清楚您的意思,那么 VB 或 JScript 可能是更适合您的语言。它们更多的是“按我的意思做,而不是按我说的做”的语言。
Paul is correct. In most situation in C# we reason "from inside to outside".
That it is unambiguous to you is irrelevant to the compiler. The task of overload resolution is to determine whether the method group
Frobble
can be resolved to a specific method given known arguments. If we can't determine what the argument types are then we don't even try to do overload resolution.Method groups that just happen to contain only one method are not special in this regard. We still have to have good arguments before overload resolution can succeed.
There are cases where we reason from "outside to inside", namely, when doing type analysis of lambdas. Doing so makes the overload resolution algorithm exceedingly complicated and gives the compiler a problem to solve that is at least NP-HARD in bad cases. But in most scenarios we want to avoid that complexity and expense; expressions are analyzed by analyzing child subexpressions before their parents, not the other way around.
More generally: C# is not a "when the program is ambiguous use heuristics to make guesses about what the programmer probably meant" language. It is a "inform the developer that their program is unclear and possibly broken" language. The portions of the language that are designed to try to resolve ambiguous situations -- like overload resolution or method type inference or implicitly typed arrays -- are carefully designed so that the algorithms have clear rules that take versioning and other real-world aspects into account. Bailing out as soon as one part of the program is ambiguous is one way we achieve this design goal.
If you prefer a more "forgiving" language that tries to figure out what you meant, VB or JScript might be better languages for you. They are more "do what I meant not what I said" languages.
我相信这是因为 C# 编译器 通常不会回溯。
I believe its because the C# compiler won't typically backtrack.
NamespaceOne 和 NamespaceTwo 在同一代码文件中定义。这相当于将它们放在不同的代码文件中并通过 using 语句引用它们。
在这种情况下,您就会明白为什么名称会发生冲突。您在两个不同的命名空间中具有同名的枚举,并且编译器无法猜测它是哪一个,即使 Frobble 有一个 NamespaceOne.MyEnum 参数。而不是
使用
NamespaceOne and NamespaceTwo are defined in the same code file. That would be equivalent to putting them in different code files and referencing them via using statement.
In that case you can see why the names clash. You have equally named enum in two different namesapces and the compiler can't guess which one it is, even though Frobble has a NamespaceOne.MyEnum parameter. Instead of
use