Question about ambiguous calls in C#
Asked Answered
K

3

9

I have a question that's not really a problem, but something that made me a little curious.

I have a class with two methods in it. One is a static method and the other one is an instance method. The methods have the same name.

public class BlockHeader
{
    public static BlockHeader Peek(BinaryReader reader)
    {
        // Create a block header and peek at it.           
        BlockHeader blockHeader = new BlockHeader();
        blockHeader.Peek(reader);
        return blockHeader;
    }

    public virtual void Peek(BinaryReader reader)
    {
        // Do magic.
    }
}

When I try to build my project I get an error saying:

The call is ambiguous between the following methods or properties: 'MyApp.BlockHeader.Peek(System.IO.BinaryReader)' and 'MyApp.BlockHeader.Peek(System.IO.BinaryReader)'

I know that the method signatures are virtually the same, but I can't see how I possibly could call a static method directly from an instance member.

I assume that there is a very good reason for this, but does anyone know what that reason is?

Kynan answered 20/5, 2009 at 9:30 Comment(9)
I think this is because J# and other languages do allow you to call static methods from instance members. The CLR has to enforce the lowest common denominator.Begone
I did not know that J# allowed this. Interesting.Kynan
I find this bizarre as well. The spec is extremely clear that this behaviour is correct. See Section 7.5.5.1, the bit that goes: "If the best method is a static method, the method group must have resulted from a simple-name or a member-access through a type..." Notice how that bit happens AFTER the bit where we figure out the "best" method. "Bestness" is clearly computed FIRST, before staticness is checked. Therefore bestness does not take staticness into account, and in your case, no best method can be determined. Therefore, ambiguity error.Screens
Anyway, you asked for the reason. The reason for this bizarre-seeming behaviour is to solve what we call "The Color Color Problem". When you have "Color Color = Color.Red; Color.Foo();", does that mean "call the static method Foo on type Color", or does it mean "call the instance method Foo on local Color"? The compiler allows both, and therefore what "Color" means in "Color.Foo()" cannot be determined until Foo is resolved. This implies that Foo must be resolved BEFORE we know exactly what "Color" means. In your case, it cannot be resolved unambiguously.Screens
See Section 7.5.4.1 for more information about the Color Color problem.Screens
Also, Vizu's answer below raises a good point. The code you give here doesn't even compile because the duplicate signatures are illegal. Did you post the wrong code here?Screens
Eric Lippert: Thank your for your very insightful answer. The code above do not compile due to duplicate signatures.Kynan
Eric Lippert: You should copy what you've written to a answer so I can set it as the accepted answer.Kynan
FYI, I'll be blogging about this on June 29th.Screens
D
9

The general policy of the C# design is to force you to specify wherever there is potential ambiguity. In the face of refactoring tools that allow one to rejig whether things are static or not at the drop of a hat, this stance is great - especially for cases like this. You'll see many other cases like this (override vs virtual, new for shadowing etc.).

In general, removing this type of room for confusion will make the code clearer and forces you to keep your house in order.

EDIT: A good post from Eric Lippert discusses another reason for this ambiguity leading to the error you saw

Danube answered 20/5, 2009 at 9:36 Comment(1)
The classic example (in this case) being an instance method that calls Peek(...); is that this.Peek? or BlockHeader.Peek?Jehias
B
4

Here's a excerpt from the C# 3.0 language specification.

The signature of a method must be unique in the class in which the method is declared. The signature of a method consists of the name of the method, the number of type parameters and the number, modifiers, and types of its parameters. The signature of a method does not include the return type.

The 'static' modifier is not part of the signature so your example violates this rule of unique signatures.

I don't know the reason behind the rule, though.

Brooklet answered 20/5, 2009 at 10:8 Comment(0)
V
2

I think there's no technical reason to disallow it, but it is done more so to protect the programmer from himself. Consider the following example:

public static void Main()
{
  BlockHeader BlockHeader = new BlockHeader();
  BlockHeader.Peek();
}

The example above is perfectly valid, but if the situation you describe were allowed, would it be readable? Could you see, in the blink of an eye, whether the instance method or the static method was called?

Vulpecula answered 20/5, 2009 at 9:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.