5

Why am I not allowed to have a static and non-static methods with the same signature?

Let's say I have a class like this

public class TestClass
{
    public object thing { get; set; }

    public TestClass()
    {

    }

    public TestClass(object thing)
    {
        this.thing = thing;
    }

    public static TestClass ConvertTestClass(object thing)
    {
        return new TestClass(thing);
    }

    public TestClass ConvertTestClass(object thing)
    {
        this.thing = thing;
        return this;
    }

    
}

and I try to use it like this

public class SomeOtherClass
{
    public SomeOtherClass()
    {
        TestClass tc = TestClass.ConvertTestClass(new object());

        TestClass tc2 = new TestClass();
        tc2.ConvertTestClass(new object());
    }
}

I get the following errors on TestClass.ConvertTestClass(new object());

The call is ambiguous between the following methods or properties: 'TestClass.ConvertTestClass(object)' and 'TestClass.ConvertTestClass(object)'

and these errors on tc2.ConvertTestClass(new object());

The call is ambiguous between the following methods or properties: 'TestClass.ConvertTestClass(object)' and 'TestClass.ConvertTestClass(object)'

Member 'TestClass.ConvertTestClass(object)' cannot be accessed with an instance reference; qualify it with a type name instead

Can the compiler really not tell the difference between the static and non static versions of that method or am I missing something here?

I am not using ReSharper (which seemed to be the root of a similar problem in other questions).

Community
  • 1
  • 1
Brad
  • 11,934
  • 4
  • 45
  • 73
  • 3
    What would you expect to be called with just `ConvertTestClass(new object())` from inside `TestClass`? ;) But in the end it was probably a design decision for the sake of clarity. – Marvin Apr 14 '15 at 22:53
  • @Marvin that certainly would be ambiguous. But, wouldn't saying either `this.ConvertTestClass` or `TestClass.ConvertTestClass` resolve the ambiguity? Sometimes we are asked to do that sort of clarification for the compiler anyway (`System.Windows.Shapes.Path`/`System.IO.Path`) – Brad Apr 14 '15 at 22:55
  • 1
    Have a look at this [answer](http://stackoverflow.com/a/6033530/3052062) by Jon Skeet – chomba Apr 14 '15 at 23:07
  • @chomba dang, my Google foo has failed me. Thanks for the link. – Brad Apr 14 '15 at 23:12
  • The only reason I would say this *isn't* a duplicate is that Jon Skeet's excellent answer refers to calls inside a class, not external calls such as the example provided here. A bit on the fence about it. If you want me to close it as a duplicate though, I will. – BradleyDotNET Apr 14 '15 at 23:23
  • @BradleyDotNET I guess the answers are the same while the question is kind of different...either way. – Brad Apr 14 '15 at 23:34

2 Answers2

4

Its giving you an error, so its a safe bet that the compiler can't, or won't, discern between the two methods.

Its probably a bad idea to do this kind of overload anyways, as it's unclear which method you are intending to invoke, but if that isn't enough, the C# 5 specification defines a method signature like this (Section 3.6):

The signature of a method consists of the name of the method, the number of type parameters and the type and kind (value, reference, or output) of each of its formal parameters, considered in the order left to right. For these purposes, any type parameter of the method that occurs in the type of a formal parameter is identified not by its name, but by its ordinal position in the type argument list of the method. The signature of a method specifically does not include the return type, the params modifier that may be specified for the right-most parameter, nor the optional type parameter constraints.

It doesn't explicitly mention static, but it also doesn't include it as part of the definition of a "signature". Thus, its fair to assume that by the spec, a method overload cannot differ only in the fact that it is static or instanced.

BradleyDotNET
  • 60,462
  • 10
  • 96
  • 117
  • Section 7.6.4 and Section 10.6.2 *seem* to suggest the compiler can tell the different. I mean this is routinely allowed `this.thing = thing;`. – Brad Apr 14 '15 at 23:12
  • @Brad Reading those now, but I never said the compiler didn't understand how `static` worked, just that it wasn't part of the signature for purposes of overloading. – BradleyDotNET Apr 14 '15 at 23:17
  • Yeah, I hear ya. This is just going to be "that's the way it is" kind of thing probably. – Brad Apr 14 '15 at 23:20
  • @Brad After reading it, it *still* doesn't mention the overloads. At best 10.6.2 indicates that it shouldn't matter because static methods can only be called via the type name, and instances through an instance. So its not actually an ambiguous call, though the overload rules suggest otherwise. – BradleyDotNET Apr 14 '15 at 23:21
0

I'd write this as a comment however it's easier to make this point in a proper editor.

I think you're only thinking about the logic of calling methods on the class externally i.e. from another class. Within the class methods with the same signature only differing by static doesn't make any sense. e.g you have a class with two methods as follows

public class MyClass
{
    public static void HellowWorld()
    {
        Console.WriteLine("Hello World!");
    }

    public void HellowWorld()
    {
        Console.WriteLine("Howdy World!");
    }

    public void Greet()
    {
        HellowWorld();
    }
} 

When compiling you'll see as long as one of the methods is commented out it compiles without errors. You should be able to alternate the commented out method and compile the class succesfully. Indicating there's no way of differentiating which method should be called within the scope of the class.

You could argue that within the class you should be forced to use the same syntax to call a static method as you do externally e.g.

MyClass.HelloWorld();

However, this would defy scoping logic used throughout C#, why should you need to specify the class name within a class? I think such a change would also create ambiguity where the was none, and to do so now would of course break a lot of code out there.

I think the compiler logic as it is makes perfect sense.

Mick
  • 6,527
  • 4
  • 52
  • 67