views:

296

answers:

6

Here is something that I find myself using from time to time and I just wanted to get some feedback on the merits of the practice.

Lets say that I have a base class:

abstract class RealBase {
    protected RealBase(object arg) {
        Arg = arg;
    }

    public object Arg { get; private set; }

    public abstract void DoThatThingYouDo();
}

I often create a second base class that is generic that handles the cast from the "object" type in the base class to the "T" type, like this:

abstract class GenericBase<T> : RealBase {
    protected GenericBase(T arg)
        : base( arg ) {
    }

    new public T Arg { get { return (T) base.Arg; } }
}

This allows me to access "Arg" as its explicit type without a cast operation:

class Concrete : GenericBase<string> {
    public Concrete( string arg )
        : base( arg ) {
    }

    public override void DoThatThingYouDo() {
        // NOTE: Arg is type string. No cast necessary.
        char[] chars = Arg.ToLowerInvariant().ToCharArray();  
        // Blah( blah, blah );
        // [...]
    }

}

All the while being able to also work with it via the "RealBase":

class Usage {
    public void UseIt() {
        RealBase rb = new Concrete( "The String Arg" );
        DoTheThing(rb);
    }

    private void DoTheThing(RealBase thingDoer) {
        rb.DoThatThingYouDo();
    }
}

It is assumed that there are many other "Concrete" types... not just the one.

Here are my questions/concerns:

  1. Am I "off my rocker" for using an approach like this?
  2. Are there any obvious drawbacks/caveats to using this approach?
  3. What about that "new public T..." in GenericBase? Good/bad idea? Awkward?

Any feedback or advice would be greatly appreciated.

+5  A: 

I don't have any objection to that explicitly as long as you're disciplined enough to only use the generic base class as a helper only and never downcast to it. If you start referencing RealBase and GenericBase and ConcreteClass all over the place things tend to get real tightly coupled really quickly.

As a matter of fact, I would recommend kicking it up a notch and introducing an interface

interface IReal {
  void DoThatThingYouDo();
}

And leaving the base class out of it entirely (basically never reference it except when declaring a derived class). Just a tip that helps me increase the flexibility of my code.

Oh, and if you do use an interface, don't just declare it in the base classes, declare it on the concrete ones:

class MyConcrete: BaseClass<MyConcrete>, IReal {
  ...
}

as a reminder, the base class is not important only what it does is important!

George Mauer
Re: declaring interfaces on concrete implementations. I have found my self debating this on and off. On one hand there is duplication, on the other communication. I think your point about de-emphasizing the importance of the base class in this case bears significant merit.
Swim
DRY (Don't Repeat Yourselves) serves a purpose but in this case I can't really see a scenario where explicitly stating interface definitions would lead you astray
George Mauer
+3  A: 

Well, we've now got an inheritance tree three levels deep, but you haven't given any particular reason for doing this.

Personally I rarely use inheritance (or rather, rarely design my own inheritance hierarchies beyond implementing interfaces and deriving directly from object). Inheritance is a powerful tool, but one which is difficult to use effectively.

If this gives you some clear advantage over other approaches, that's fine - but I would consider the complexity you're adding for someone reading the code. Do they really want to have to consider three levels of hierarchy, with two properties of the same name? (I would rename GenericBase.Arg to GenericBase.GenericArg or something like that.)

Jon Skeet
I agree. Professors ~love~ inheritance and create nice, neat and ultimately unrealistic examples of its usage. I think it can take some new graduates some time to realize that every program they write doesn't need to have 6 levels of inheritance.
Simucal
Jon, what's your opinion of using base classes as helpers just so you don't have to repeat yourself? I do this frequently but admittedly have nightmares of someone coming along later and starting downcasting and passing MyBaseClass references around.
George Mauer
George: If the base class doesn't contain any state or abstract methods, I'd write a utility class with static methods instead. If you really do have abstract methods or state you can reuse, that's more reasonable. I just don't find myself in that situation very often.
Jon Skeet
(An alternative is to *compose* the reusable bit of code, of course. That will sometimes work and sometimes not - it depends on the details.)
Jon Skeet
Hmm, I use base classes all the time for example in my entities to do things like override Equals or define the Id property. I really don't see the problem if nobody ever downcasts to BaseEntity
George Mauer
If I decide Id should be a Guid rather than an int I'd rather have to change it in just the base entity and those entities that don't use it than in every single entity
George Mauer
I don't write many entity types :)
Jon Skeet
Entity types spring forth from Jon Skeets loins all on their own?
George Mauer
A: 

Personally I would strongly advise not to use the new operator, since it may lead to confusion. Actually I myself have recently had such a problem, but I went with base class abstract suffixed with AsObject, i.e.:

public BaseClass{
    public abstract object ValueAsObject {get;set;}
}

and generic class along the line:

public BaseClass<T> : BaseClass {
    public T Value {get;set;}
    public override object ValueAsObject {
       get{return (T)this.Value;} 
       set{this.Value = value;} // or conversion, e.g. string -> int
}

Georg Mauer's proposal of interface for DoThatThingYouDo is also good.

+2  A: 

I think you would be able to get the same functionality you would like by using an interface as opposed to dual abstract base classes, consider this:

public interface IAmReal
{
    void DoThatThingYouDo();
    ...
}

abstract class GenericBase<T> : IAmReal
{
    protected GenericBase<T>(T arg)
    {
        Arg = arg;
    }
    public T Arg { get; set; }
    public abstract void DoThatThingYouDo();
}

class MyConcrete : GenericBase<string>
{
    public MyConcrete(string s) : base(s) {}
    public override void DoThatThingYouDo()
    {
        char[] chars = Arg.ToLowerInvariant().ToCharArray();
        ...
    }
}

class Usage
{
    public void UseIt()
    {
        IAmReal rb = new MyConcrete( "The String Arg" );
        DoTheThing(rb);
    }

    private void DoTheThing(IAmReal thingDoer)
    {
        rb.DoThatThingYouDo();
    }
}
Quintin Robinson
+1  A: 

I don't think you're off your rocker. See Curiously Recurring Template for something even more complex.

Joshua
"struct Derived : Base<Derived>" just caused my stack to overflow :)
Swim
A: 

Am I the only one who thinks inheritance should be avoided as far as it can? I've seen different implementations when inheritance have caused bizarre problems, not only when trying to downcast. Interfaces are the savior! I'm not saying that inheritance should always be avoided, but by just looking at the specified code I can't see the advantages of this inheritance tree over regular interfaces.

Björn