Polymorphism & Collections

J

Jerad Rose

I'm relatively new to C# and polymorphism, so what I'm trying to accomplish
may not be possible, or there may be a totally different approach that I
should be taking.

I have a base class (MyBase) and a collection of MyBase objects
(MyBaseCollection). Now, I also have an extension to MyBase (MyExtendedBase
: MyBase) that has additional properties. And lastly, I have a collection
of MyExtendedBase objects(MyExtendedBaseCollection : MyBaseCollection).

I have a data layer that has a method (GetMyBaseCollection) that retrieves
records from the database, and populates a MyBaseCollection with these
records. However, there are cases where I need to use this method to
populate a MyExtendedBaseCollection. But here's the catch -- I want my data
layer to be unaware (and independent) of MyExtendedBase and
MyExtendedBaseCollection.

I have tried several different ways to accomplish this, but no matter what I
do, I end up needing to up-cast MyBase to MyExtendedBase, which is illegal
in C#. I need my data layer to be able to accept and populate a
MyBaseCollection (with it possibly being a sub class of a
MyExtendedBaseCollection) with MyBase objects (with them possibly being sub
classes of MyExtendedBase), and then when the collection is returned, the
items in the collection are of the appropriate type (depending on the type
of collection passed in).

I have read about different design patterns (namely Decorator and
Composite), but am not sure if they apply to my situation, and if so, how to
implement them accordingly.

Again, I may be doing something totally illogical, so I'm open to any
suggestions you may have. Also, if I have not provided enough info, let me
know and I will follow up.

Thanks in advance.

Jerad
 
D

dkode

I think your're pretty close. If you have a base class, then you should
have no problem passing MyExtendedBaseCollection as a MyBaseCollection
object. This is the power of polymorphism.

A couple of your statements:
need my data layer to be able to accept and populate a
MyBaseCollection (with it possibly being a sub class of a
MyExtendedBaseCollection) with MyBase objects (with them possibly being sub
classes of MyExtendedBase),

MyExtendedBase and MyExtendedBaseCollection are sub classes of MyBase
and MyBaseCollection.

When you say a "subclass" it means that it inherits from a base class
or abstract class. I think you have the terminology backwards.

If you are trying to cast MyBase to MyExtendedBase, this is not going
to work. When you need to pass around a specialized type of class, but
dont want to code additional methods (apart from special functionality)
then you cast your dervied or subclasses (MyExtendedBase) to type
MyBase, you can then pass it around as normal, any methods that are in
the base class that you override in the subclass, can be called as
normal because both the base class and sub class have definitions for
those methods.

Another way to accomplish this would be to use interfaces.

As far as the design patterns, this is something I am still learning as
well, I have found that if you dont understand the design pattern
completely, then dont use it at all. because chances are is that you
will miuse the pattern, and create more headaches then solutions.

hope my comments help!
 
H

Helge Jensen

Jerad said:
do, I end up needing to up-cast MyBase to MyExtendedBase, which is illegal

That's a down-cast. It's "legal" (staticly valid), but if the actual
object instance casted is not an instance of MyExtendedBase the cast
will fail and throw an exception.
Again, I may be doing something totally illogical, so I'm open to any
suggestions you may have. Also, if I have not provided enough info, let me
know and I will follow up.

Let the data-layer, that should be generic in the types, accept the
data-structure to fill items into as an argument. If your data layer
needs to construct "unknown" type objects from data you can have a look
at the factory-pattern. The data-layer can accept, as a parameter, a
factory that is given the data to parse and create an object from that.

BTW: Are you in C#1 or 2?

Decorator and composite are completely unrelated to your problem.
 
B

Bob

Hi Jerad,
You only need one collection. The one for the base class.
When you're pulling in data from the database test the attribute(s) that
mark the difference between Mybase and MyExtendedBase before instantiating
the object that you are going to add to the collection.
bool MyDatabaseGetRoutine(ref collection col)
{
SQlDataCommand etc setup.
MyBase b;
While MyDatareader.read
{
If(MyDataReader.GetString(say5) == dbNull) //Instantiate a MyBase
b=new MyBase();
else // Instantiate a MyBaseExtended
b= new MyBaseExtended();
// Now fill the common (MyBase) properties
b.Common1 = MyDataRead.GetString(0);
b.Common2 = MyDataRead.GetString(1);
Test again for type.
if (b.GetType().ToString()=="MyBaseExtended")

{

// Fill the extended properties

}

col.add(b); Adding to collection regardless of Instantiation.

When you come to use the collection iterate across it with a base type
variable

bool LetsUnpackIt(collection col)



foreach (mybase b in col)

{

if (b.gettype().ToString()=="MyBase")

//Do the mybase things

else

// Do the myBaseExtended things

HTH

Bob
 
J

Jerad Rose

Thanks for the responses, guys.



To clarify on a few comments:


When you say a "subclass" it means that it inherits
from a base class or abstract class. I think you have
the terminology backwards.



Yeah, I guess I was thinking the "subclass" would be the more generic one
and have fewer members, sort of like a subset. Thanks for the
clarification.


I have found that if you dont understand the design
pattern completely, then dont use it at all.



I tend to agree with you there, which was why I wouldn't have used them
unless somebody could have helped me understand them.



Helge said:
That's a down-cast. It's "legal" (staticly valid), but
if the actual object instance casted is not an instance
of MyExtendedBase the cast will fail and throw an
exception.



Yeah, that's what I meant (see example below). But I did get the
up-/down-cast confused. But I think I've seen other people posting this
backwards as well.



Helge said:
The data-layer can accept, as a parameter, a
factory that is given the data to parse and create
an object from that.



It seems this is the recommended way to go. And I had thought about going
this route originally, but the thing I don't like about using a factory is
that my data layer is dependent on this factory, which it turn is dependent
BTW: Are you in C#1 or 2?



1.1


You only need one collection. The one for the
base class.



Theoretically true, but I want my collections to be type-safe, so that
casting isn't required to access the extended members. However, if doing
this would remove the dependence of my data layer on my extended class, I
would go this route.


When you're pulling in data from the database
test the attribute(s) that mark the difference between
Mybase and MyExtendedBase before instantiating
the object that you are going to add to the collection.



This is similar to the factory concept, and requires the same dependency of
my data layer on my extended object/properties. The data layer will not
need access to any of the extended properties, so I would prefer to not
require this dependency. But again, I may not have that option.



Repeating what I said in my original post, here is my setup:



MyBase

MyBaseCollection

MyExtendedBase : MyBase

MyExtendedBaseCollection : MyBaseCollection



I can pass an instance of MyExtendedBaseCollection to the data layer, caste
as MyBaseCollection. But when my data layer creates new MyBase objects and
adds them to the collection, there's no way I can down-cast them to
MyExtendedBase when I return from the data layer. I would think there would
be a way to have a base collection get populated with base objects, without
introducing the dependency of the underlying method on the extended
class/collection. It seems this would be a common need, but it sounds like
my options aren't as elegant as "best practices" encourage.



Dkode, you mentioned interfaces. Would the use of interfaces help
accomplish my goals?



Thanks again.



Jerad
 
W

wfairl

If you want "type safety" (more likely you want to avoid the upcast
here) then you should consider a move to 2.0 and generics. You can do
something similar to this, I've done it in my own data
objects/collections

Something like:
MyBaseObj{ }
MyBaseCollection<T> : ICollection<T> where T : MyBaseObj
{
public T this[int index]
{
get
{
...
}
}

...
}

Then MyDerivedObj :MyBaseObj for your derived record obj
and MyDerivedCollection : MyBaseCollection<MyDerivedObj> for your
derived collection of derived record objects
 
D

dkode

Thats interesting how you accomplish this with generics. I need to read
up more on generics. All I have used them for is for a List collection
within another class. What does using the <T> accomplish? You don't
have to specify the type?
 
J

Jon Skeet [C# MVP]

dkode said:
Thats interesting how you accomplish this with generics. I need to read
up more on generics. All I have used them for is for a List collection
within another class. What does using the <T> accomplish? You don't
have to specify the type?

You don't specify the type when writing the generic type; you specify
it when you *use* the type.

It's well worth reading up on generics - it's a massive topic though!

Jon
 
J

Jerad Rose

For those interested, I am posting below what ended up being the ultimate
solution I came to. This accomplishes both of my goals:

1) Type safety
2) Independence of data layer on extended classes

Your feedback is welcomed and appreciated.

Thanks.
Jerad

//Polymorphic Collections Example

public class MyBaseClass
{
//base member implementation
}

public class MyBaseCollection : ICollection
{
protected ArrayList _innerList;

public MyBaseClass this[int index]
{
get { return (MyBaseClass)_innerList[index]; }
}

public virtual MyBaseClass AddNewItem()
{
MyBaseClass newBaseItem = new MyBaseClass();
_innerList.Add(newBaseItem);
return newBaseItem;
}

//standard collection member implementation
}

public class MyExtendedClass : MyBaseClass
{
public string MyExtendedProperty
{
//get/set implementation
}
}

public class MyExtendedCollection : MyBaseCollection
{
public new MyExtendedClass this[int index]
{
get { return (MyExtendedClass)base._innerList[index]; }
}

public override MyBaseClass AddNewItem()
{
MyExtendedClass newExtendedItem = new MyExtendedClass();
_innerList.Add(newExtendedItem);
return newExtendedItem;
}
}

public class MyDataLayer
{
public void GetRecords(MyBaseCollection myBaseCol)
{
//database implementation

while(dr.Read())
{
//This is critical -- using the collection's AddNewItem
//to instantiate the object will ensure the proper type
//is being instantiated. If the collection is an
//instance of MyExtendedCollection, it will create and
//add an instance of MyExtendedClass to the collection.
//Otherwise, it will add an instance of MyBaseClass.

MyBaseClass myBase = myBaseCol.AddNewItem();

//set myBase's base properties
}
}
}

public class TestClient
{
public void Main()
{
MyDateLayer myDL = new MyDataLayer();

//This will pass an instance of a base collection to the data layer
MyBaseCollection myBaseCol = new MyBaseCollection();
myDL.GetRecords(myBaseCol);
myBaseCol[0].MyExtededProperty = "Not legal";

//This will pass an instance of an extended collection to the data
layer
MyExtendedCollection myExtCol = new MyExtendedCollection();
myDL.GetRecords(myExtCol);
myExtCol[0].MyExtededProperty = "Legal";
}
}
 
D

dkode

I have a quick question about your implementation.

Doesn't this violate n-tier design because the datalayer has knowledge
of the business layer objects? I thought the data layer was only
supposed to return records (DataReader) in this instance, or is all of
this code in your data layer?
From other NG postings, I've had people tell me that the Data Layer has
NO knowledge of business layer objects, doing so violates proper N-Tier
design and tighly couples your business layer with your data layer.
 
J

Jerad Rose

We use following layers, from bottom to top:

Business Entities
Data Layer
Business Layer
Client

So, our data layer *is* aware of the business entities, but *not* the
business layer. The business entity layer is just the logical schema for
our data. So, instead of our data layer returning a generic set of records
(DataReader or DataSet), it returns typed objects representing our data.
Then the business layer applies any business logic/rules to the data before
returning it to the client.
 
D

dkode

I see. Is there any code duplication between the Business Entities
structure and the Business Layer?
 
J

Jerad Rose

No, these are completely independent of each other. The business entity
classes only consist of typed properties and collections, representing our
data schema. The business layer uses the data layer to populate the
business entities, and then carries out any business logic implementation
needed.
 
D

dkode

I see how you have it configured now.

I have never tried to setup my apps that way, I've always had the
business entities in the same layer as the business rules. I guess this
does allow for better seperation, and for getting typed data from your
data layer rather then generic datareader/dataset.

Have you had success with structuring your apps in this way? I might
opt to try this in my next project (planning stages now)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top