leppie
leppie

Reputation: 117220

Overload resolution oddity

Not sure if this is C# 4+ specific, but just noticed this.

Consider the following classes:

class Base
{
  protected void Foo(object bar, DayOfWeek day)
  {
  }
}

class Program : Base
{
  protected void Foo(object bar, object baz)
  {
  }

  void Bar(DayOfWeek day)
  {
    Foo(new { day }, day);
  }
}

The call to Foo in Bar, resolves to Foo(object, object).

While changing it to:

class Base
{

}

class Program : Base
{
  protected void Foo(object bar, object baz)
  {
  }

  protected void Foo(object bar, DayOfWeek day)
  {
  }

  void Bar(DayOfWeek day)
  {
    Foo(new { day }, day);
  }
}

The call to Foo in Bar, resolves to Foo(object, DayOfWeek).

My understanding is that it should always resolve as in the second example.

Is this a 'bug' or just my lack of understanding (or ignorance)?

Update:

Thanks for the answers. As I have found out, one can use base. to call the method in the base class. The problem comes back however when adding another derived class in the mix.

class Base
{
  protected void Foo(object bar, DayOfWeek day)
  {
  }
}

class Program : Base
{
  protected void Foo(object bar, object baz)
  {
  }

  void Bar(DayOfWeek day)
  {
    base.Foo(new { day }, day);
  }
}

class Derived : Program
{
  void Baz(DayOfWeek day)
  {
    base.Foo(new { day }, day);
  }
}

The base. call works in Program, but then resolves to Foo(object, object) in Derived.

How would one call Foo(object,DayOfWeek) from Derived then without having to create 'redundant' methods in Program ?

Upvotes: 6

Views: 191

Answers (4)

Enigmativity
Enigmativity

Reputation: 117027

You need to checkout Eric Lippert's blog - particularly this article: https://learn.microsoft.com/en-us/archive/blogs/ericlippert/future-breaking-changes-part-three

Effectively though, the overload resolution algorithm searches the current class for an overload that can be called and only searches the base classes for alternatives when one isn't found in the current class.

In the first case the Foo(object, object) overload is applicable so no further searching is performed.

In the second case the Foo(object DayOfWeek) is better so it is used.

Read Eric's article for the full detail.

Upvotes: 4

Eren Ersönmez
Eren Ersönmez

Reputation: 39085

As others noted, the problem is related to the way overload resolution works. Just to add to that:

If Base.Foo were public, then you could do this to get to Base.Foo within Derived (assuming Base.Foo wasn't overriden):

((Base)this).Foo(new { day }, day);

Also, you have the option of overriding (if you can change Base.Foo to be virtual) or explicitly hiding Base.Foo within Program so when you call base.Foo within Derived, it still calls Base.Foo :

class Program : Base
{
  protected void Foo(object bar, object baz)
  {
  }

  protected new void Foo(object bar, DayOfWeek baz)
  {
    base.Foo(bar, baz);
  }

  void Bar(DayOfWeek day)
  {
    base.Foo(new { day }, day);
  }
}

As a side note: Usually, derived classes provide overloads with more specific parameter types than their base class overloads (e.g. Object.Equals(object), String.Equals(string)).

Even if there's a case for less (or equally) specific parameter types, they'd either override the base method (e.g. Object.Equals(object), String.Equals(object) -> overrides Object.Equals(object)), or they just give it a different method name.

Upvotes: 1

Cheng Chen
Cheng Chen

Reputation: 43523

I think the spec makes sense. The derived class programmer doesn't need to know the unrelative methods' implementation in base classes. Otherwise, an unlucky guy wrote a method with less compatiable than the one in its base classes(and this guy doesn't know the details of the base classes), the method in the base class is invoked then.

Upvotes: 1

Habib
Habib

Reputation: 223237

I think for resolving method call it looks in its class first, since DayOfWeek can be passed as object type, it calls class own method, not the one from the base class.

In the second case, the method call resolves to a the more particular type parameter, therefore Foo(object bar, DayOfWeek day) gets called.

From MSDN - Method resolution.

methods in a base class are not candidates if any method in a derived class is applicable (Section 7.5.5.1).

  • Given the set of applicable candidate function members, the best function member in that set is located.
  • If the set contains only one function member, then that function member is the best function member.
  • Otherwise, the best function member is the one function member that is better than all other function members with respect to the given argument list, provided that each function member is compared to all other function members using the rules in Section 7.4.2.2.
  • If there is not exactly one function member that is better than all other function members, then the function member invocation is ambiguous and a compile-time error occurs.

Upvotes: 6

Related Questions