Generic collections and inheritance

  • Thread starter Thread starter Lars
  • Start date Start date
Lars said:
But I still would like to get to the bottom of this generic issue. I have
rewritten the sample following your suggestions. Instead of naming
the base classes Contract and Contract<T>, I have named them
ContractBase and ContractBase<T>. Here is the updated sample:

[code snipped]
Note that LeaseContract needs to inherit from Contract

What you have renamed to "ContractBase" *is* your Contract class, and
LeaseContract should inherit from it via ContractBase said:
but as
coded above it does not work right since the line types then
becomes ContractLine and not LeaseContractLine. I basically
need to be able to subclass more than one level from the
abstract base class.

You can subclass from LeaseContract just fine, no?
Also, I don't quite see why "public abstract class ContractBase"
is needed.

It's needed because it's your base 'Contract' class. It's the place you
put all the virtual methods etc. to support polymorphism for all your
contracts.

In other words, rather than having this hierarchy (using <: to represent
subtype):

LeaseContract <: Contract <: ContractBase<Contract> <: ContractBase

.... you should have this:

LeaseContract <: ContractBase<LeaseContract> <: Contract

All the generic stuff goes on ContractBase<>, all the dynamic dispatch
(i.e. virtuals etc.) / polymorphic stuff goes on Contract.

-- Barry
 
You can subclass from LeaseContract just fine, no?
No, not from what I can tell. If you inherit from LeaseContract
(which is not generic), how do you specify the type? <TLine>
It's needed because it's your base 'Contract' class. It's the place you
put all the virtual methods etc. to support polymorphism for all your
contracts.

In other words, rather than having this hierarchy (using <: to represent
subtype):

LeaseContract <: Contract <: ContractBase<Contract> <: ContractBase

... you should have this:

LeaseContract <: ContractBase<LeaseContract> <: Contract
Unless I am missing something, this still does not solve the problem
of not being able to have more than one level of subclassing
from the generic base class, since you can only specify <TLine>
when inheriting directly from it.

Another point that I did not mention in my previous email is that
I would very much like for the subclasses to not be generic,
to avoid a LOT of refactoring of existing classes and to keep
the application code cleaner (the generic syntax is lengthier and
harder to read).

Thanks,
Lars
 
Lars said:
No, not from what I can tell. If you inherit from LeaseContract
(which is not generic), how do you specify the type? <TLine>
in the ContractBase<TLine> class will be of type LeaseContractLine
and not of the subclass-line type.

Sorry for being obtuse, but how could you respecify it even in
old-fashioned non-generic world? Suppose this:

class LeaseContract
{
public LeaseContractLineCollection Lines { ... }
}

How do you derive from this and change the type of the collection
without violating type safety - that is, without introducing the
problems associated with covariance?
Unless I am missing something, this still does not solve the problem
of not being able to have more than one level of subclassing
from the generic base class, since you can only specify <TLine>
when inheriting directly from it.

This is the point behind my original question:

"a) Now, a question you've got to answer: do you still need to be able
to add arbitrary ContractLine instances to a LeaseContract?"

The reason I asked this question was to figure out if you needed an
inheritance hierarchy with the lines - and show that you had a
covariance problem if you did. Has your answer changed? :)
Another point that I did not mention in my previous email is that
I would very much like for the subclasses to not be generic,

LeaseContract isn't a generic class, though, right? It's only the base
to avoid a LOT of refactoring of existing classes and to keep
the application code cleaner (the generic syntax is lengthier and
harder to read).

I reckon that writing your own typed collections - like the
LeaseContractLineCollection - is lengthier and harder to maintain, much
less read, than generics. YMMV.

-- Barry
 
You can subclass from LeaseContract just fine, no?
Sorry for being obtuse, but how could you respecify it even in
old-fashioned non-generic world?

Here is a sample what I did, which seems to work fine (create
a simple form with a DataGridView named uxDataGrid and try it):

using System;
using System.Collections;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;

namespace RegularInheritanceTest
{
public partial class Form6 : Form
{
public Form6()
{
InitializeComponent();

LeaseContract contract = new LeaseContract();
contract.AddLine(new LeaseContractLine());
uxDataGrid.DataSource = contract.Lines;
// The grid displays both ContractLineTestProperty &
// LeaseContractLineTestProperty...
}
}

// Contract & ContractLine class:
public class Contract
{
protected ContractLineCollection _lines = new
ContractLineCollection();
public Contract() { }

public void AddLine(ContractLine line)
{
_lines.Add(line);
}
public ContractLineCollection Lines { get { return _lines; } }
}

public class ContractLine
{
public int ContractLineTestProperty { get { return 1; } }
public ContractLine() { }
}

// LeaseContract & LeaseContractLine class:
public class LeaseContract : Contract
{
public LeaseContract() { }
}

public class LeaseContractLine : ContractLine
{
public int LeaseContractLineTestProperty { get { return 2; } }
public LeaseContractLine() { }
}

// ContractLineCollection class:
public class ContractLineCollection : CollectionBase
{
public void Add(ContractLine item)
{
List.Add(item);
}

public void Remove(int index)
{
if ((index > (Count - 1)) || (index < 0))
throw (new Exception("Index out of range: " + index));
List.RemoveAt(index);
}
}
}

This is the point behind my original question:

"a) Now, a question you've got to answer: do you still need to be able
to add arbitrary ContractLine instances to a LeaseContract?"

The reason I asked this question was to figure out if you needed an
inheritance hierarchy with the lines - and show that you had a
covariance problem if you did. Has your answer changed? :)
No, but let me try to clarify: If I create a LeaseContract, I will only
add LeaseContractLines to it, and if I create some other (further
sub-classed) class, I will only add lines belonging to that level
of subclassing to it. I will not mix different type of ContractLines.
But I need to be able to subclass more than one level deep from the
generic base class.
LeaseContract isn't a generic class, though, right? It's only the base
class that's generic in this design - i.e. ContractBase<>, yes?
Yes, but LeaseContract as written in my previous sample does not work -
it only stores ContractLines. It needs to store LeaseContractLines.
In order for it to store LeaseContractLines I need to do this:
public class Contract : ContractBase<ContractLine>
but then I am back to only being able to subclass directly from
ContractBase<T>...

Thanks for your patience,
Lars
 
Lars said:
No, but let me try to clarify: If I create a LeaseContract, I will only
add LeaseContractLines to it, and if I create some other (further
sub-classed) class, I will only add lines belonging to that level
of subclassing to it.

The trouble with this scheme is that you can evade it by casting your
SubLeaseContract (an example subclass of LeaseContract) to a
LeaseContract type, and then freely add LeaseContractLine instances.
Basically, there's no way to create this hierarchy in a statically
type-safe way - you need to use polymorphism and runtime type-checking
to get it right.

This is one reason generics can't help you much in your situation -
they're only good for static type safety.

One way out would be to make a generic LeaseContract<> descendant, so
that you get this picture:

class LeaseContract<TLine> : Contract<TLine> // ...
class LeaseContract : LeaseContract<LeaseContractLine> // ...
class SubLeaseContract : LeaseContract<SubLeaseContractLine> // ...

.... but there's still no way to treat a SubLeaseContract as a
LeaseContract, because that would violate type safety - the covariance
problem. A way out would be to create an interface, ILeaseContract, and
implement it in different places (although LeaseContract<TLine> should
do). It would need to be somewhat similar to IList, in that it would
deal with some common denominator type, and the implementations check
the type at runtime.

-- Barry
 

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

Back
Top