Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# abstract method with base class only implementation?

Consider the following problem:

You have a class 'A' that serves as a base class for a lot of other similar classes, eg. a class called B.

The class A is useful in it self (and already used all over the place) and is hence not abstract.

The crux is that you now wish to enforce a new protocol requiring all classes inheriting from A (including A itself) to implement a method 'func()'. If a sub class forgets to implement func() there should be a compiler error.

class A {
  A func() { ret new A(...) }
}

class B : A {
  A func() { ret new B(...) }
}

class C : A {
  // Should give error : no func()
}

Is the problem clear? I can't make A::func() abstract since I want to be able to keep using A as a concrete class. I can't make it virtual since that would cause sub classes to fall back on the super class (and not give compiler errors).

The only thing that comes close to a solution is creating a new abstract class A* and have all custom types inherit from that and replace all current usages of A as a concrete class with a new class 'DefaultA' that inherits from A*. This seems messy. Please tell me there is some other way to do this?

like image 286
4ZM Avatar asked Sep 15 '10 14:09

4ZM


2 Answers

There is simply no way to both keep A a concrete type and force a compiler error if sub types do not override a particular method.

like image 112
JaredPar Avatar answered Oct 21 '22 07:10

JaredPar


Well, as you say, you could create an intermediate abstract class X that inherits from A and require that derived classes inherit from X. X then declares a protected abstract method with the signature matching func and override the func from A to call that method. Here's an example:

class A {
    public virtual A func() { ... }
}

abstract class X : A {
    protected abstract A funcImpl();

    public override A func() { return funcImpl(); }
}

class B : X  { /* must now implement funcImpl() to avoid compiler error */ }

class C : X  { /* must now implement funcImpl() to avoid compiler error */ }

The main problem with this approach is requiring that all derived class now inherit from X rather than A. So you've just changed the enforcement problem from one kind to another.

A cleaner approach, in my opinion, is to refactor A into a base class ABase and have func be abstract there. Then seal A and require B and C to inherit from ABase rather than A:

class ABase {
    public abstract ABase func();    }

sealed class A : ABase {
    public override ABase func() { ... }
} 

class B : ABase { 
    // forced to override func()
    public override ABase func() { ... }
}

You would have to replace all uses of A with ABase - but there are some excellent refactoring tools in Visual Studio (and third party tools like ReSharper) that make this substantially less painful than it once was.

like image 41
LBushkin Avatar answered Oct 21 '22 07:10

LBushkin