I have just seen on the comment to a blog post:
Base abstract generic class is a bad choice in most situations
Is this true,开发者_如何转开发 if not why?
What insight(s) leads to this statement?
I agree, because anything that inherits an abstract generic class will not be polymorphic with the base class. That is, if you have
abstract class myBase<T>
then you create
class myThing: myBase<thing>
class myOtherThing: myBase<otherThing>
you can't create methods that work against myThing and myOtherThing since they do not share an ancestor. There's no point in the base class being abstract, really, it might as well just be a class.
But if you have a base class
abstract class myBase
class myBase<T>: myBase
as is a common pattern for generic classes (like IEnumerable - using interfaces), then they all share myBase.
(edit) I just read the actual blog post - and actually, the comment is not really valid in that situation. The "abstract generic base class" he's referring to, Range<T>
inherits IEnumerable<T>
which inherits non-generic interface IEnumerable
. So it's not really an "abstract generic base class." But generally I think it's true.
"Most situations" is outrightly vague. A generic abstract class (or interface) is a bad idea if the only common ancestor between descendants of such class is System.Object (as noted by other commenters of this question).
Otherwise (as in, if you do have a meaningful common ancestor), it's a good idea if you want to "rename" or "specialize" members. Consider this example:
// Meaningful common ancestor for the working classes.
interface IWorker
{
object DoWork();
}
// Generic abstract base class for working classes implementations.
abstract WorkerImpl<TResult> : IWorker
{
public abstract TResult DoWork();
object IWorker.DoWork()
{
return DoWork(); // calls TResult DoWork();
}
}
// Concrete working class, specialized to deal with decimals.
class ComputationWorker : WorkerImpl<decimal>
{
override decimal DoWork()
{
decimal res;
// Do lengthy stuff...
return res;
}
}
In this example, DoWork()
was redefined in the abstract class, becoming concrete and specialized in ComputationWorker
.
One problem with abstract generic base class is that you can't type decorate :
public abstract class Activity<TEntity>
{
public Activity() { }
protected virtual object Implementation { ... }
}
public abstract class CompensableActivity<TEntity,TCompensation> : Activity<TEntity>
where TCompensation : Activity<T>, new()
{
public CompensableActivity() { }
protected override object Implementation
{
get { new Wrapper(base.Implementation, Compensation); }
}
private Activity<TEntity> Compensation
{
get
{
var compensation = new TCompensation();
if(compensation is CompensableActivity<TEntity,Activity<TEntity>)
{
// Activity<TEntity> "does not meet new() constraint" !
var compensable = comp as CompensableActivity<TEntity, Activity<TEntity>>;
var implement = compensable.Implementation as Wrapper;
return implement.NormalActivity;
}
else { return compensation; }
}
}
}
Kragen makes a good point. Anything that is less than 50% of your code is "the wrong choice in the majority of situations".
However, even though nowhere near 50% of your classes should be generic abstract base classes, it also isn't less of a good thing than any other language feature.
For example, BindingList<T>
could be considered an abstract generic base class. It's a generic container, and sorting requires you to derive from it and override ApplySortCore
.
KeyedCollection<TKey, TItem>
doesn't just act like an abstract generic base class, it is one. To use it you MUST derive from it and implement GetKeyForItem
.
I disagree with the crowd here. In some cases an abstract generic class is the best design. A good example in .NET can be found in System.Collections.ObjectModel where the KeyedCollection allows you to override and implement a typed serializable dictionary collection easily!
I elided most of the code but this is the principle:
public class NameCollection : System.Collections.ObjectModel.KeyedCollection<string, INamedObj>
{
protected override string GetKeyForItem(INamedObj item)
{
return item.Name;
}
}
Well, statistically speaking if someone asked me "should I make this class an abstract generic class?", the answer would almost certainly be no - in 3 years of .Net development I think I can count the number of abstract classes that I've written that had generic type parameters on one hand.
Other than that I can't see any particular reason for an abstract generic class to be considered a Bad Thing - its just not that common.
There are situations where an abstract base class is useful thing.
We have several .net applications that use different database engines. Several sql server, a couple of mysql and a bunch of oracle apps.
We have a generic common database object which is based on an abstract class that is a factory that returns the proper database object in a factory setting based on the type of database.
That way if I am starting a new application, all I have to do is load this database object, pass in the type, pass in the connection string, and bam... i'm set...
I guess what I'm trying to say is it all depends on the context and how its actually used.
精彩评论