Odrade
Odrade

Reputation: 7609

c#: Why isn't this ambiguous enum reference resolved using the method signature?

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.

Upvotes: 9

Views: 1714

Answers (3)

Eric Lippert
Eric Lippert

Reputation: 660038

Paul is correct. In most situation in C# we reason "from inside to outside".

there is no ambiguity in what method is being called,

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.

Upvotes: 14

Juan Ayala
Juan Ayala

Reputation: 3518

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

MethodNamespace.MethodClass.Frobble(MyEnum.foo)

use

MethodNamespace.MethodClass.Frobble(NamespaceOne.MyEnum.foo)

Upvotes: 1

Paul Batum
Paul Batum

Reputation: 8415

I believe its because the C# compiler won't typically backtrack.

Upvotes: 6

Related Questions