implementing polymorphism in c#, how best to do it

2020-07-23 08:58发布

问题:

first question here, so hopefully you'll all go gently on me!

I've been reading an awful lot over the past few days about polymorphism, and trying to apply it to what I do in c#, and it seems there are a few different ways to implement it. I hope I've gotten a handle on this, but I'd be delighted even if I haven't for clarification.

From what I can see, I've got 3 options:

  1. I can just inherit from a base class and use the keyword 'virtual' on any methods that I want my derived classes to override.
  2. I could implement an abstract class with virtual methods and do it that way,
  3. I could use an interface?

From what I can see, if I don't require any implementation logic in the base, then an interface gives me the most flexibility (as I'm then not limiting myself with regards multiple inheritance etc.), but if I require the base to be able to do something on top of whatever the derived classes are doing, then going with either 1 or 2 would be the better solution?

Thanks for any input on this guys - I have read so much this weekend, both on this site and elsewhere, and I think I understand the approaches now, yet I just want to clarify in a language specific way if I'm on the right track. Hopefully also I've tagged this correctly.

Cheers, Terry

回答1:

An interface offers the most abstraction; you aren't tied to any specific implementation (useful if the implementation must, for other reasons, have a different base class).

For true polymorphism, virtual is a must; polymorphism is most commonly associated with type subclassing...

You can of course mix the two:

public interface IFoo {
    void Bar();
}
class Foo : IFoo {
    public virtual void Bar() {...}
}
class Foo2 : Foo {
    public override ...
} 

abstract is a separate matter; the choice of abstract is really: can it be sensibly defined by the base-class? If there is there no default implementation, it must be abstract.

A common base-class can be useful when there is a lot of implementation details that are common, and it would be pointless to duplicate purely by interface; but interestingly - if the implementation will never vary per implementation, extension methods provide a useful way of exposing this on an interface (so that each implementation doesn't have to do it):

public interface IFoo {
    void Bar();
}
public static class FooExtensions {
    // just a silly example...
    public static bool TryBar(this IFoo foo) {
        try {
             foo.Bar();
             return true;
        } catch {
             return false;
        }
    }
}


回答2:

All three of the above are valid, and useful in their own right. There is no technique which is "best". Only programming practice and experience will help you to choose the right technique at the right time.

So, pick a method that seems appropriate now, and implement away. Watch what works, what fails, learn your lessons, and try again.



回答3:

Interfaces are usually favored, for several reasons :

  • Polymorphisme is about contracts, inheritance is about reuse
  • Inheritance chains are difficult to get right (especially with single inheritance, see for instance the design bugs in the Windows Forms controls where features like scrollability, rich text, etc. are hardcoded in the inheritance chain
  • Inheritance causes maintenance problems

That said, if you want to leverage common functionnality, you can use interfaces for polymorphism (have your methods accept interfaces) but use abstract base classes to share some behavior.

public interface IFoo
{
    void Bar();
    enter code here
}

will be your interface

public abstract class BaseFoo : IFoo
{
    void Bar
  {
        // Default implementation
  }
}

will be your default implementation

public class SomeFoo : BaseFoo
{

}

is a class where you reuse your implementation.

Still, you'll be using interfaces to have polymorphism:

public class Bar
{
   int DoSometingWithFoo(IFoo foo)
{

    foo.Bar();
}
}

notice that we're using the interface in the method.



回答4:

The first thing you should ask is "why do I need to use polymorphism?", because polymorphism is not and end by itself, but a mean to reach an end. Once you have your problem well defined, it should be more clear which approach to use.

Anyway, those three aproaches you commented are not exclusive, you still can mix them if you need to reuse logic between just some classes but not others, or need some distinct interfaces...



回答5:

  • use abstract classes to enforce a class structure
  • use interfaces for describing behaviors


回答6:

It really depends on how you want to structure your code and what you want to do with it.

Having a base class of type Interface is good from the point of view of testing as you can use mock objects to replace it.

Abstract classes are really if you wish to implement code in some functions and not others, as if an abstract class has nothing other than abstract functions it is effectively an Interface.

Remember that an abstract class cannot be instantiated and so for working code you must have a class derived from it.

In practice all are valid.

I tend to use an abstract class if I have a lot of classes which derive from it but on a shallow level (say only 1 class down).

If I am expecting a deep level of inheritence then I use a class with virtual functions.

Eitherway it's best to keep classes simple, along with their inheritence as the more complex they become the more likelyhood of introducing bugs.