简体   繁体   中英

C# derived classes, overload resolution

Ok, I have an some different objects that are derived from a base class and I've put a bunch of them in a list. I want to loop through the list and push each to a method. I have separate methods with each one's type signature, but the compiler is complaining. Can someone explain why? Is this an opportunity to use Generics, and if so, how?

class Base { }
class Level1 : Base { }
class Level2 : Level1 { }

...

List<Base> oList = new List<Base>();
oList.Add(new Level1());
oList.Add(new Level2());

...

...
foreach(Base o in oList)
{
   DoMethod(o);
}

...

void DoMethod(Level1 item) { }
void DoMethod(Level2 item) { }

What am I doing wrong?

Overloads are resolved at compile-time - and you don't have a DoMethod(Base item) method - so it can't resolve the call. Leaving the list and the loop out of things, you're effectively writing:

Base o = GetBaseFromSomewhere();
DoMethod(o);

The compiler has to find a method called DoMethod which is applicable for a single argument of type Base . There is no such method, hence the failure.

There are a few options here:

  • As Markos says, you can use dynamic typing in C# 4 to make the C# compiler apply overloading at execution time using the actual type of object that o refers to.
  • You can use the Visitor Pattern to effectively get double dispatch (I'm never really fond of this)
  • You can use as or is :

     Level1 x = o as Level2; if (x != null) { DoMethod(x); // Resolves to DoMethod(Level1) } else { Level2 y = o as Level2; if (y != null) { DoMethod(y); // Resolves to DoMethod(Level2) } } 

    Again, this is pretty ugly

  • Redesign what you're doing to be able to use normal inheritance, if possible

Overloading a method uses the static type of the variable not the run time type.

You want to use inheritence and overriding .

class Base { public virtual void DoMethod() { /* ... */  } }
class Level1 : Base { public override void DoMethod() { /* ... */ } }
class Level2 : Level1 { public override void DoMethod() { /* ... */ } }

Which method is called is determined in Compile time not runtime, so the compiler cannot know which one to call. You have 2 options: Switch over type of the object and call apropriate method, or if you are using .NET 4, use type dynamic.

foreach(dynamic o in oList)
{
   DoMethod(o);
}

You don't have a DoMethod(Base item) method. Overloading is not polymorphic. This is normally done by using a virtual method:

class Base {
    public virtual void DoMethod() {...}
}
class Level1 : Base {
    public override void DoMethod() {...}
}
// etc..

foreach(Base o in oList)
{
    o.DoMethod();
}

In your foreach loop, o has type Base and neither of the DoMethod overloads take a Base instance. If possible you should move DoMethod to Base and override it in the two subclasses:

public class Base
{
    public virtual void DoMethod() { ... }
}

为了扩展Mark的答案,DoMethod应该是Base中的一个虚方法,您可以在列表中的每个项目上调用它。

I don't know all the details, but if it's a situation where it really isn't appropriate to inherit you could use interfaces instead.

Declare the interface, implement it on each of your classes, then you would be able to cast directly to the interface and run the function from there. My C# is a little shaky, but something like,

Interface IMethodizable
{
   void DoMethod();
}

class Level1 : IMethodizable {
  void DoMethod(){
    //insert code here
  }
}

class Level2 : IMethodizable {
  void DoMethod(){
    //insert code here
  }
}

This works particularly well if the only thing the classes have in common are that method. This is very similar to having a virtualized method int he base class and overriding it. So this pattern is only better if you shouldn't be inheriting, or the DoMethod will also have to run on other objects not inheriting from base, et al.

Since C# 7.0 pattern-matching is another option.

For more information see MSDN . Your code would like like:

switch(o)
{
    case Level2 level2: Do(level2); break;
    case Level1 level1: Do(level1); break;
    case Base @base: Do(@base); break;
    default: ...
    case null: ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM