Question

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 ?

Was it helpful?

Solution

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.

OTHER TIPS

You need to checkout Eric Lippert's blog - particularly this article: http://blogs.msdn.com/b/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx

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.

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top