9

If I have a class SubOfParent which is a sub-class of Parent, and two methods:

 public static void doStuff(Parent in) {}

 public static void doStuff(SubOfPArent in) {}

why does the first doStuff get called when I pass a SubOfParent type object?

Thanks for any insight on this!

Bruno Antunes
  • 2,241
  • 4
  • 21
  • 35

9 Answers9

12
Parent p = new SubOfParent();
SubOfParent s = new SubOfParent();

doStuff(p); //calls doStuff(Parent in)
doStuff(s); //calls doStuff(SubOfParent in)

//If you're working with a parent but need to call the subclass, you need to upcast it.
dostuff(p as SubOfParent);
Mark H
  • 13,797
  • 4
  • 31
  • 45
  • 4
    It's obvious in this example that p is of type SubOfParent, but that won't always be the case. To avoid any surprises in doStuff it's better to upcast, check for null, then make the call. SubOfParent subOfParent = p as SubOfParent; if (subOfParent!= null) doStuff(subOfParent); – Dave M Jul 16 '10 at 13:41
  • This makes sense in some cases, but when trying to use overloading in a Strategy-ish pattern it fails miserably; for instance, you can't have the code automatically choose between SubOfParent and SubOfParent2 overloads given a variable of type Parent that is assigned one of the derived types. – KeithS Feb 14 '12 at 16:48
8

method overloading is done at compile time, not at run time, so you won't see any polymorphism with it. Whatever the calling code knows the type to be is going to specify which method is called.

if you want to call the second method you can either cast to the type SubOfPArent or you can instead put those methods into the classes and then call it to get polymorphism:

public class Parent 
{
  public virtual void doStuff() {}
}

public class SubOfPArent : Parent
{
  public override void doStuff() {}
}

...

Parent obj = getASubOfPArent();
obj.doStuff();
tster
  • 17,883
  • 5
  • 53
  • 72
  • 2
    Question is about static functions, not instance functions. – apoorv020 Jul 16 '10 at 13:14
  • 1
    @apoorv020 tster still answered the question correctly. Although it is true that static method overloads are handled slightly differently that instance methods, it doesn't make a difference in this case. – Adam Houldsworth Jul 16 '10 at 13:16
  • 1
    @apoorv020, that's why I said that he could move the functions. Obviously I am recommending that he change the way the code is structured. – tster Jul 16 '10 at 13:16
4

You probably called the method with a variable of type Parent.

Since method overloads are resolved at compile time, the compiler can only select an overload based on the static compile-time types of the parameters.

Therefore, even though your variable might actually contain a SubOfParent instance at runtime, the compiler doesn't know that and will therefore choose the first overload.

Virtual methods, by contrast, are resolved at runtime based on the actual type of the instance involved.
Therefore, had SubOfParent overridden a virtual method, calling that method on a variable of type Parent would correctly call the overridden method if the instance is in fact of type SubOfParent.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • I passed SubOfParent - for sure. The method that calls these two has if( a.GetType() == typeof(SubOfParent) ) { doStuff(a); } else { doStuff(a); } and debugging through the code, it definitely goes through the first doStuff(a). – Bruno Antunes Jul 16 '10 at 13:09
  • Ah, if you are having to type check then yes the *runtime* type might be `SubOfParent` but I suspect the compile-time type is `Parent` - in C# overload choice is defined at compile-time. – Adam Houldsworth Jul 16 '10 at 13:12
  • That's the difference between function overloading and multiple dispatch. – Bertrand Marron Jul 16 '10 at 13:13
  • @sardaukar: Since the `a` variable is declared as `Parent`, the compiler will always call the first overload. – SLaks Jul 16 '10 at 13:14
2

I think you are casting to Parent in your code, or otherwise providing a Parent type. So I think the behaviour is correct and what you are expecting to happen is incorrect.

It is correct that both overloads are valid for a SubOfParent reference, but the overload resolution logic will look for more specific methods and should therefore find the most specific overload.

SubOfParent p = new SubOfParent();

doStuff((Parent)p); // Calls base overload
doStuff(p); // Calls specific overload
Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187
2

If you're ok with using the dynamic keyword, you could consider doing something like this:

private static void doStuffImpl(Parent obj) {
   Console.WriteLine("I'm a Parent!");
}

private static void doStuffImpl(SubOfParent obj) {
   Console.WriteLine("I'm a Sub of Parent!");
}

public static void doStuff(Parent obj) {
    try {
      doStuffImpl(obj as dynamic);
    }
    catch (RuntimeBinderException ex) {
        // Not implemented !
    }
}

It could be useful if you have a lot of sub classes.

doStuffImpl(obj as dynamic); will be evaluated at run-time. doStuffImpl will be called with obj's real type.

Bertrand Marron
  • 21,501
  • 8
  • 58
  • 94
1

Method overloading is done at Compile time and therefore depends on the static type at compile time to determine the overloaded method. In your example, the following could happen:

public static void Main(string[] args)
{
    SubOfParent a = new SubOfParent();
    doStuff(a); // doStuff(SubOfParent) is called
}

The static type of a at compile time is SubOfParent, so the expected overload would be called.

public static void Main(string[] args)
{
    Parent a = new SubOfParent();
    doStuff(a); // doStuff(Parent) is called
}

The static type (that of the type at declaration) is Parent. In this case, the other overloaded version would be chosen regardless of the value a.GetType() returns at runtime.

Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
0

To call doStuff(SubOfPArent in) you need something like this:

SubOfPArent.doStuff(new SubOfPArent());

But I think you dont know the type until runtime right?

What tster says is more elegant. I think that is the right thing.

Felipe Pessoto
  • 6,855
  • 10
  • 42
  • 73
0

Basically, the compiler resolves what method gets called on an object (i.e., when that object is passed as a parameter) based on that object's declared type. So if you have a variable typed as Parent and you pass it to doStuff, the compiler will resolve that method call to be the overload taking a Parent, even if at runtime that object turns out to be a SubOfParent.

Method calls by an object (i.e., instance methods of a class) exhibit polymorphism at runtime: the method executed is based on the object's actual type.

So if you had this:

class Parent
{
    public virtual void doStuff() { }
}

class SubOfParent : Parent
{
    public override void doStuff() { }
}

Parent p  = new SubOfParent();
p.doStuff();

Then the code would work as you expect.

Dan Tao
  • 125,917
  • 54
  • 300
  • 447
0

you can overload functions by declaring multiple function with same name and different arguments

Sagar Varpe
  • 3,531
  • 4
  • 27
  • 43