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 ! 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!
<insert rebuke about data-centric design here >
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.
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!
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.
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.
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, ) 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