Generics & Inheritance (again)

Generics & Inheritance (again)

Old forum URL: forums.lhotka.net/forums/t/1976.aspx


CaymanIslandsCarpediem posted on Thursday, December 14, 2006

I've been using CSLA 1.x for some time and am finally going to take the leap to 2.x.  My biggest question (which seems the same can be said for many) is how to do inheritance chains with generics.  I've read all the previous posts on the subject (a bit of information overload). 

With all the talk in previous posts about using interfaces instead of inheritance I'll really quickly explain why I'm using inheritance.  If anyone can explain how to use interfaces to accomplish this I'll be glad to try it. 

First off, I'm using inheritance because of common data. I know, I'm bad Devil [6]!  Its all supposed to be modeled on behaviour, but what can I say.  Here is an example of why I'm using inheritance.  I have many different objects which can all have n number of documents associated with them.   I have a base class which has all the functionality for dealing with these documents and I then inherit from that base class so I get that document functionality for "free" (no real extra code in the child classes).  I REALLY don't want to use an interface where I would then have to duplicate this basic document functionality (association and basic CRUD operations) in each class which implements that interface.

So thats the "why" (again would be happy to hear other options), now for the question of "how" in CSLA 2.x.  Everything I've been reading here is that generics and inheritance just don't play nicely together.  So my question is: if when I define the "base" class I make that a concrete type can I then just inherit the base class from the children?  Will this cause any issues? 

So if I define the base class as:

public abstract class BaseObject : BusinessBase<BaseObject>

Can I then define any/all levels of child objects the "old" way like:

public class ChildObject : BaseObject

And if I do go this route, am I giving anything up by not trying to use generics all the way through the inheritance chain?

Thx!

RockfordLhotka replied on Thursday, December 14, 2006

<insert rebuke about data-centric design here Stick out tongue [:P]>

I think you'll be happiest if you create non-generic base classes as high in your inheritance hierarchy as possible and just live without generics for all your business classes. Otherwise could end up creating generic and non-generic version of each of your objects and that'd be a pain.

Product
Product<-ServiceProduct
Product<-TaxableProduct
Product<-ServiceProduct<-TaxableService

You get something like that, where you really want to be able to instantiate any of those objects, but you also want the inheritance, and generics will seriously complicate matters.

You can create your own custom BusinessBase by inheriting directly from Csla.Core.BusinessBase - bypassing generics all the way down.

For collections and other base classes you'll probably have to start with a generic base class and create a non-generic subclass that will be your base class for all subsequent classes.

xal replied on Thursday, December 14, 2006

I guess it would really depend on what you have to do.
If you have a situation such as the one rocky mentions, it's going to be a pain with generics, since you can't make instances of generic classes. But if those base classes are not supposed to work in stand alone mode (meaning, create an instance of a base class), then definitely go with generics.

So, assuming that everything that is called "xyzBase" is not something that you will create an instance of and use it like a stand alone object, you should be good.

If you still need to make that Base class and be able to make an instance of it but don't want to loose the advantages of generics, it can be solved by having something like this:

Public Class ProductBase(Of T)
    Inherits Csla.BusinessBase(Of T)
<<All the code here>>
End Class

Public Class Product
    Inherits ProductBase(Of Product)
<< No Code Here other than the factory >>
<< methods and perhaps an override of GetIdValue() >>
End Class


That way, you have a generally inheritable generic class that you can use for pretty much anything you need, and another non-generic class with not really any code in it, other than the factory methods, that you can use in as a regular business object in your ui...


This is of course pointless if you need not to create an instance of product and you are only ever going to use it as a base class, in which case you don't need that second class at all and generics wouldn't ever get into your way...

Andrés

CaymanIslandsCarpediem replied on Thursday, December 14, 2006

Thanks all, thats basically what I figured.  Sounds like I should be able to not have any big issues in my case (my base class is abstract).  Cheers!

 

Michael Hildner replied on Thursday, December 14, 2006

CaymanIslandsCarpediem:
I have many different objects which can all have n number of documents associated with them.   I have a base class which has all the functionality for dealing with these documents and I then inherit from that base class so I get that document functionality for "free" (no real extra code in the child classes).  I REALLY don't want to use an interface where I would then have to duplicate this basic document functionality (association and basic CRUD operations) in each class which implements that interface.

Maybe I don't get it but why not have your objects have an instance variable/property that's a collection of documents? e.g. DocumentEditableChildList and DocumentEditableChild.

CaymanIslandsCarpediem replied on Thursday, December 14, 2006

Well, really I could.  The biggest issue for me is its not just documents, but there are roughly 10 of these common data items hanging off the base class and there are roughly 20 objects which all need these common items.  So I'd have to create all these properties as well other tasks (update calls, etc) in each child class.  I'm not crazy about this from a maintance perspective with all the duplicated code. 

However, I may still go that route.  This question is really about "can" I do it this way (especially initially just to make the upgrade to smoother).  Realisticly I may well do it with interfaces yet, I just wanted to make sure the inheritance option was open to me.  With .NET partial classes and code generation I think I can just generate all of that logic so the duplication/maintance worries kind of go away.  Really, I'm liking that idea and its my goal to get there I'm just not sure I'll do that as part of my inital upgrade to CSLA 2.x.

SonOfPirate replied on Friday, December 15, 2006

Just my two cents...

I use Generics EXTENSIVELY in my class libraries.  I am true lover of the template design - maybe it's because I come from the C/C++ world originally  (via fortran, Ick! [+o(])  Anyway, here's a couple of rules-of-thumb that I have adopted to help me keep the oft-chaotic world of Generics under control:

1. I never create a Generic interface.  The reason? Because Generics are a total pain when it comes to determining an object's type in code.  For instance, if you want to know if something is enumerable, you will want to use a condition such as: if (myClass is IEnumerable) and not: if (myClass is IEnumerable<T>).  Fact is that this will fail unless it is contained in a Generic class with type T defined.  So, to eliminate this headache, any custom interface I create is non-generic.  This means that any class, whether Generic or not, that implements the interface can be handled in a non-Generic way.

2. All of my Generic classes are marked as abstract (MustInherit in VB).  The reason?  For one, the class was created to be a base class, this just forces it to be.  By marking a class abstract, it allows you to inherit from the class but still adhere to the implement interfaces rule.  This is because abstract classes are considered in the same "family" as an interface in OOP.  It's not concrete.

3. Only implement a class as a Generic if some property or method MUST have knowledge of the concrete class (derived class).  This is the case with collections (where Generics are prevalent) because the base class methods either accept or return objects of a specific type.

Inheritance with Generics is really no different than inheritance without.  What you need to decide is whether your derived class will also serve as a base class or if it is the concrete class.  If it is the concrete class, then you'd use the approach Andrés described: public MyClass : BaseClass<MyClass> and the Generic chain is gone and hidden from this point forward.  On the other hand, if you are creating a new class that you intend to also serve as a base class, just carry the Generic forward, e.g. public MyBaseClass<T> : SomeOtherBaseClass<T>.

I do this often.  One example is my SelectableCollectionBase class which contains a collection of selectable items (implementing my ISelectable interface).  The class is declared as:

public abstract class SelectableCollectionBase<T, C> : CslaBusinessListBase<T, C>
  where T : SelectableCollectionBase<T, C>
  where C : ISelectable
{
:
}

In regards to your specific example, the question to ask yourself when determining if your "BaseObject" should be Generic or not is to see if you are accepting or returning any BaseObject arguments.  If you are then you are setting yourself up to code in casting whenever you use those properties or methods.  This is because you'll be passing in or want the return value to be in your concrete class, ChildObject, and the only way to do that is to cast it from BaseObject to ChildObject.  If you want to eliminate this need, then make BaseObject Generic as well as these properties/methods will be strongly-typed to your ChildObject and no more casting.  And, keep in mind that these may not be new properties/methods that you've implemented in BaseObject, but by defining the class non-generically, you've set all the Generic properties and methods in the base class(es) to BaseObject as well.

Which brings me to my last rule-of-thumb:

4. Never use a Generic class as a concrete class.  This is just a preference and a way to evaluate what I am doing.  This is also a hard one to adhere to as there are plenty of times when there is good reason to violate this rule, such as List<T> and Nullable<T> - which I do use often.  But, when designing my applications, if I'm going to have to instantiate List<MyObject>, then I would rather create a MyObjectCollection class that inherits from BusinessListBase<T, C> and I am no longer instantiating a Generic class as a concrete class.

Anyway, these are just the rules I follow. Hopefully I've justified my thinking a little and along the way helped you understand how Generics fit into the spectrum.  As I said, I make extensive use out of them as I see them as a powerful tool to help you build your object model.

HTH

 

Copyright (c) Marimer LLC