By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
434,931 Members | 1,247 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 434,931 IT Pros & Developers. It's quick & easy.

Generic collections and inheritance

P: n/a
Hi,

I have a base class holding a generic list that needs
to be accessed by both the base class and its subclasses.
What is the best solution to this?

I am fairly new to generics, but I am aware of that fact
that if you have a class B, that inherits from A, then
List<Bdoes NOT inherit from List<A>. So I understand
why the example below does not compile, but I fail to
see how to solve the problem stated above... For example,
I need to be able to return a BindingList<LeaseContractLine>
from the LeaseContract class below.
// Sample:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;

namespace GenericInheritanceTest
{
public partial class Form1 : Form
{
// A simple form with only a DataGridView on it:
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
LeaseContract leaseContract = new LeaseContract();
// Display the contract lines in a DataGridView:
uxLeaseContractGrid.DataSource = leaseContract.GetLines();
}
}

public class Contract
{
protected BindingList<ContractLine_lines = new
BindingList<ContractLine>();
public Contract() { }
//
// methods providing functionality for all types of contracts...
//
}

public class LeaseContract : Contract
{
public LeaseContract() { }

public BindingList<LeaseContractLineGetLines()
{
// Will not compile (since the cast is not valid):
return (BindingList<LeaseContractLine>)_lines;
}
//
// specific LeaseContract methods, etc.
//
}

public class ContractLine
{
public ContractLine() { }
}
public class LeaseContractLine : ContractLine
{
public LeaseContractLine() { }
}
}
Any help would be greatly appreicated!

Thanks,
Lars
Jul 21 '06 #1
Share this Question
Share on Google+
25 Replies


P: n/a
I would try making Contract itself a generic class:
Something like this:

public class Contract<Twhere T : ContractLine
{
protected BindingList<T_lines = new BindingList<T>();
}

public class LeaseContract : Contract<LeaseContractLine>
{
....
}
--
Adam Clauss

"Lars" <no*****@dev.nullwrote in message
news:zK******************************@megapath.net ...
Hi,

I have a base class holding a generic list that needs
to be accessed by both the base class and its subclasses.
What is the best solution to this?

I am fairly new to generics, but I am aware of that fact
that if you have a class B, that inherits from A, then
List<Bdoes NOT inherit from List<A>. So I understand
why the example below does not compile, but I fail to
see how to solve the problem stated above... For example,
I need to be able to return a BindingList<LeaseContractLine>
from the LeaseContract class below.
// Sample:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;

namespace GenericInheritanceTest
{
public partial class Form1 : Form
{
// A simple form with only a DataGridView on it:
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
LeaseContract leaseContract = new LeaseContract();
// Display the contract lines in a DataGridView:
uxLeaseContractGrid.DataSource = leaseContract.GetLines();
}
}

public class Contract
{
protected BindingList<ContractLine_lines = new
BindingList<ContractLine>();
public Contract() { }
//
// methods providing functionality for all types of contracts...
//
}

public class LeaseContract : Contract
{
public LeaseContract() { }

public BindingList<LeaseContractLineGetLines()
{
// Will not compile (since the cast is not valid):
return (BindingList<LeaseContractLine>)_lines;
}
//
// specific LeaseContract methods, etc.
//
}

public class ContractLine
{
public ContractLine() { }
}
public class LeaseContractLine : ContractLine
{
public LeaseContractLine() { }
}
}
Any help would be greatly appreicated!

Thanks,
Lars

Jul 21 '06 #2

P: n/a
"Adam Clauss" <ca*****@tamu.eduwrote in message
news:12*************@corp.supernews.com...
>I would try making Contract itself a generic class:
Something like this:

public class Contract<Twhere T : ContractLine
{
protected BindingList<T_lines = new BindingList<T>();
}

public class LeaseContract : Contract<LeaseContractLine>
{
...
}
Thanks for the reply - that seems to solve that problem. When
trying to implement this in the actual application, I come
across another problem, though: The ContractLine class holds
a reference to the parent (the Contract class). Trying to
assign to this reference gives a compile error in AddLine() below:
"Cannot convert type 'Contract<T>' to 'Contract<ContractLine>'"
(abbreviated message).

Here is the updated sample code:

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

namespace GenericInheritanceTst
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
}

// Contract & ContractLine class:
public class Contract<Twhere T : ContractLine, new()
{
protected BindingList<T_lines = new BindingList<T>();
public Contract() { }

public void AddLine()
{
T contractLine = new T();
// Won't compile ("Cannot convert type 'Contract<T>' to
'Contract<ContractLine>'"):
contractLine.Parent = this;
_lines.Add(contractLine);
}
}

public class ContractLine
{
public Contract<ContractLineParent;
public ContractLine() { }
}

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

public BindingList<LeaseContractLineGetLines()
{
return _lines;
}
}

public class LeaseContractLine : ContractLine
{
public LeaseContractLine() { }
}
}

What am I missing?

Thanks,
Lars
Jul 21 '06 #3

P: n/a
"Lars" <no*****@dev.nullwrote in message
news:b9******************************@megapath.net ...
Thanks for the reply - that seems to solve that problem. When
trying to implement this in the actual application, I come
across another problem, though: The ContractLine class holds
a reference to the parent (the Contract class). Trying to
assign to this reference gives a compile error in AddLine() below:
"Cannot convert type 'Contract<T>' to 'Contract<ContractLine>'"
(abbreviated message).
Heh... incidentally someone else is trying to do something very similar.
See the post by Kris Jennings (dated 07/21/2006 8:51pm) - more specifically
see the reply to that by Barry Kelly (9:28pm).

In any event, the ContractLine probably also needs to be generic.

namespace GenericInheritanceTst
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
}

// Contract & ContractLine class:
public class Contract<Twhere T : ContractLine<T>, new()
{
protected BindingList<T_lines = new BindingList<T>();
public Contract() { }

public void AddLine()
{
T contractLine = new T();
contractLine.Parent = this;
_lines.Add(contractLine);
}
}

public class ContractLine<LineBasewhere LineBase :
ContractLine<LineBase>, new()
{
public Contract<ContractLineParent;
public ContractLine() { }
}

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

public BindingList<LeaseContractLineGetLines()
{
return _lines;
}
}

public class LeaseContractLine : ContractLine<LeaseContractLine>
{
public LeaseContractLine() { }
}
}
Generics make for very good... generic code. But they can be difficult to
wrap your mind around at times.
--
Adam Clauss
Jul 22 '06 #4

P: n/a
"Adam Clauss" <ca*****@no.spam.gmail.comwrote in message
news:OX**************@TK2MSFTNGP03.phx.gbl...

Also - unless you particularly only want "GetLines" to be in LeaseContract,
with generics you can now abstract it up to the base class:

// Contract & ContractLine class:
public class Contract<Twhere T : ContractLine<T>, new()
{
protected BindingList<T_lines = new BindingList<T>();
public Contract() { }

public void AddLine()
{
T contractLine = new T();
contractLine.Parent = this;
_lines.Add(contractLine);
}

public BindingList<TGetLines()
{
return _lines;
}
}

That should still allow what you need:
LeaseContract contract = ....
BindingList<LeaseContractLinelines = contract.GetLines();

And immediately support any future forms of Contract/ContractLine pairs.
Jul 22 '06 #5

P: n/a
In any event, the ContractLine probably also needs to be generic.
>
public class ContractLine<LineBasewhere LineBase :
ContractLine<LineBase>, new()
{
Thanks again Adam - that got me one step closer. But with a
recursive constraint for ContractLine:
public class ContractLine<Twhere T : ContractLine<T>, new()

how do you declare a reference to it? This does not compile:
List<Contract<ContractLine>contracts = new List<Contract<ContractLine>>();

You get "Using the generic type 'ContractLine<T>' requires '1' type
arguments"
(and List<Contract<ContractLine<ContractLine>>of course will not either)

Updated sample:

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

namespace GenericInheritanceTst
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
// Error: Using the generic type 'ContractLine<T>' requires '1'
type arguments:
List<Contract<ContractLine>contracts = new
List<Contract<ContractLine>>();
}
}

// Contract & ContractLine class:
public class Contract<Twhere T : ContractLine<T>, new()
{
protected BindingList<T_lines = new BindingList<T>();
public Contract() { }

public void AddLine()
{
T contractLine = new T();
contractLine.Parent = this;
_lines.Add(contractLine);
}
}

public class ContractLine<Twhere T : ContractLine<T>, new()
{
public Contract<TParent;
public ContractLine() { }
}

// LeaseContract & LeaseContractLine class:
public class LeaseContract<Twhere T : LeaseContractLine<T>, new()
{
public LeaseContract() { }
}

public class LeaseContractLine<Twhere T : LeaseContractLine<T>, new()
{
public LeaseContractLine() { }
}
}

Thanks,
Lars
Jul 22 '06 #6

P: n/a
In any event, the ContractLine probably also needs to be generic.
>
public class ContractLine<LineBasewhere LineBase :
ContractLine<LineBase>, new()
{
Thanks again Adam - that got me one step closer. But with a
recursive constraint for ContractLine:
public class ContractLine<Twhere T : ContractLine<T>, new()

how do you declare a reference to it? This does not compile:
List<Contract<ContractLine>contracts = new List<Contract<ContractLine>>();

You get "Using the generic type 'ContractLine<T>' requires '1' type
arguments"
(and List<Contract<ContractLine<ContractLine>>of course will not either)

Updated sample:

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

namespace GenericInheritanceTst
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
// Error: Using the generic type 'ContractLine<T>' requires '1'
type arguments:
List<Contract<ContractLine>contracts = new
List<Contract<ContractLine>>();
}
}

// Contract & ContractLine class:
public class Contract<Twhere T : ContractLine<T>, new()
{
protected BindingList<T_lines = new BindingList<T>();
public Contract() { }

public void AddLine()
{
T contractLine = new T();
contractLine.Parent = this;
_lines.Add(contractLine);
}
}

public class ContractLine<Twhere T : ContractLine<T>, new()
{
public Contract<TParent;
public ContractLine() { }
}

// LeaseContract & LeaseContractLine class:
public class LeaseContract<Twhere T : LeaseContractLine<T>, new()
{
public LeaseContract() { }
}

public class LeaseContractLine<Twhere T : LeaseContractLine<T>, new()
{
public LeaseContractLine() { }
}
}

Thanks,
Lars
Jul 22 '06 #7

P: n/a
"Lars" <no****@dev.nullwrote in message
news:12*************@corp.supernews.com...
Thanks again Adam - that got me one step closer. But with a
recursive constraint for ContractLine:
public class ContractLine<Twhere T : ContractLine<T>, new()

how do you declare a reference to it? This does not compile:
List<Contract<ContractLine>contracts = new
List<Contract<ContractLine>>();

You get "Using the generic type 'ContractLine<T>' requires '1' type
arguments"
(and List<Contract<ContractLine<ContractLine>>of course will not either)
Hrm... thats a very good question hehe... one I'm afraid I do not know the
answer to.
I've posted a followup to Barry in that other thread to see if he has a
suggestion.

--
Adam Clauss
Jul 24 '06 #8

P: n/a
"Adam Clauss" <ca*****@no.spam.gmail.comwrote in message news:eH***************@TK2MSFTNGP05.phx.gbl...
"Lars" <no****@dev.nullwrote in message news:12*************@corp.supernews.com...
>Thanks again Adam - that got me one step closer. But with a
recursive constraint for ContractLine:
public class ContractLine<Twhere T : ContractLine<T>, new()

how do you declare a reference to it? This does not compile:
List<Contract<ContractLine>contracts = new List<Contract<ContractLine>>();

You get "Using the generic type 'ContractLine<T>' requires '1' type arguments"
(and List<Contract<ContractLine<ContractLine>>of course will not either)
OK, this coming from suggestions by Barry and Larry in that other thread. Try extracting the public members of Contract into an
interface (IContract) which Contract would implement. Then your list would just be:

List<IContractcontracts = new List<IContract>();
--
Adam Clauss
Jul 24 '06 #9

P: n/a
OK, this coming from suggestions by Barry and Larry in that other thread.
Try extracting the public members of Contract into an interface
(IContract) which Contract would implement. Then your list would just be:

List<IContractcontracts = new List<IContract>();
Thanks for hanging in there, Adam.

I am not sure I follow the last part - if I use an interface
for IContract (or IContractLine as Barry & Larry suggested),
how will that interface represent both the base class and the
subclasses derived from it? Would I have to push all my
subclassing hierarchies into interfaces? Also, when I tried
doing this in the sample, I realized that I do not understand
quite how to do this. Could you show me an example?

Thanks,
Lars

Ps. What I am trying to do should be a fairly common
scenario, so I am surprised at the amount of complexity
generics seem to add to it. Not sure if they are worth
the added complexity... Any thoughts on this?

Jul 24 '06 #10

P: n/a
"Lars" <no****@dev.nullwrote in message news:ts******************************@megapath.net ...
Thanks for hanging in there, Adam.
Not a problem, I'm still trying to get a better handle on generics and how everythign works together with them myself, so I am
almost as interested in the solution as you (almost because I don't have an immediate need for it :) )
I am not sure I follow the last part - if I use an interface
for IContract (or IContractLine as Barry & Larry suggested),
how will that interface represent both the base class and the
subclasses derived from it? Would I have to push all my
subclassing hierarchies into interfaces? Also, when I tried
doing this in the sample, I realized that I do not understand
quite how to do this. Could you show me an example?
OK took a stab at our example code. Note following changes
Addition of interfaces IContract/IContractLine. Contract inherits from IContract and ContractLine inherits from IContractLine.
Also - the LeaseContract and LeaseContractLine classes do NOT need to be generic (as you had them in your previous post).

namespace GenericInheritanceTst
{
public partial class Form2 : Form
{
public Form2()
{
List<IContractcontracts = new List<IContract>();
}
}

interface IContract
{
void AddLine();
}

interface IContractLine
{
//not sure what kind of public methods should go here?
//You may not need an interface for IContractLine if these never make it to a generic collection like Contract does.
}

// Contract & ContractLine class:
public class Contract<T: IContract where T : ContractLine<T>, new()
{
protected BindingList<T_lines = new BindingList<T>();
public Contract() { }

public void AddLine()
{
T contractLine = new T();
contractLine.Parent = this;
_lines.Add(contractLine);
}
}

public class ContractLine<T: IContractLine where T : ContractLine<T>, new()
{
public Contract<TParent;
public ContractLine() { }
}

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

public class LeaseContractLine : ContractLine<LeaseContractLine>
{
public LeaseContractLine() { }
}
}
Ps. What I am trying to do should be a fairly common
scenario, so I am surprised at the amount of complexity
generics seem to add to it. Not sure if they are worth
the added complexity... Any thoughts on this?
Well, I agree it does have a bit of complexity. But I think it's more of a "once you figure it out" for the first time, it gets
easier.

--
Adam Clauss
Jul 24 '06 #11

P: n/a
OK took a stab at our example code. Note following changes
Addition of interfaces IContract/IContractLine. Contract inherits from
IContract and ContractLine inherits from IContractLine.
Also - the LeaseContract and LeaseContractLine classes do NOT need to be
generic (as you had them in your previous post).
Ok. I like the fact of not having to make LeaseContract
generic, since it makes for clear code. To take this
one step further, I was thinking of using an abstract
base class that is generic and have non-generic sub-
classes from that. In this way, the interface of the
Contract & ContractLine class can stay the same and
I do not have to change all the classes that are already
using them.

So I have ContractBase<Tand from that I create the
subclass Contract as follows:
public class Contract : ContractBase<ContractLine>

But then I want to create LeaseContract that should
be a subclass of Contract, but if I do that, I of course
lose the ability to specify that T should be
LeaseContractLine:
public class LeaseContract : Contract

Instead I have to do this:
public class LeaseContract : ContractBase<LeaseContractLine>
But that means I can only subclass directly from the
generic base class.

Is there any way to keep the "genericness" of the contract
classes from having to be exposed in their interface (and
with "interface" I mean "publicly exposed properties &
methods")? It would make the code using these classes
much more clear & readable, and I would not have to
refactor all my existing code using these classes.

Updated sample:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace GenericInheritanceTest4
{
public partial class Form4 : Form
{
public Form4()
{
InitializeComponent();
}
}

interface IContract
{
void AddLine();
}

interface IContractLine
{
//not sure what kind of public methods should go here?
//You may not need an interface for IContractLine if these never
make it to a generic collection like Contract does.
}

// ContractBase & ContractLineBase class:
public abstract class ContractBase<T: IContract where T :
ContractLineBase<T>, new()
{
public int ContractTestProperty;
protected BindingList<T_lines = new BindingList<T>();
public ContractBase() { }

public void AddLine()
{
T contractLine = new T();
contractLine.Parent = this;
_lines.Add(contractLine);
}
}

public abstract class ContractLineBase<T: IContractLine where T :
ContractLineBase<T>, new()
{
public ContractBase<TParent;
public ContractLineBase() { }
}

// Contract & ContractLine class:
public class Contract : ContractBase<ContractLine>
{
public int LeaseTestProperty;
public Contract() { }
}

public class ContractLine : ContractLineBase<ContractLine>
{
public ContractLine() { }
}

// LeaseContract & LeaseContractLine class:
public class LeaseContract : ContractBase<LeaseContractLine>
{
public int LeaseTestProperty;
public LeaseContract() { }
}

public class LeaseContractLine : ContractLineBase<LeaseContractLine>
{
public LeaseContractLine() { }
}
}

Thanks again,
Lars
Jul 24 '06 #12

P: n/a
OK took a stab at our example code. Note following changes
Addition of interfaces IContract/IContractLine. Contract inherits from
IContract and ContractLine inherits from IContractLine.
Also - the LeaseContract and LeaseContractLine classes do NOT need to be
generic (as you had them in your previous post).
I started to try to implement this and came accross another
problem: You cannot instantiate a Contract directly:

This does not compile:
IContract newContract = new Contract<IContractLine>();
You get "IContractLine must have a public parameterless
constructor in order to use it as parameter 'T' in the
generic type or method 'Contract<T>'," since an interface
does not have a constructor.

This compiles just fine, though:
LeaseContract leaseContract = new LeaseContract();

It seems like you have to make the generic class an abstract
base class and subclass it with non-generic classes. The
only problem is how to subclass further from such a subclass
(see my previous post in this thread).
Sample:

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

namespace GenericInheritanceTst2
{
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
// Works fine with a collection:
List<IContractcontracts = new List<IContract>();

// But how do you create a reference to a single contract??:
IContract newContract = new Contract<IContractLine>();

// Creating a reference to LeaseContract works just fine:
LeaseContract leaseContract = new LeaseContract();
}
}

public interface IContract
{
void AddLine();
BindingList<IContractLineGetLines();
}

public interface IContractLine
{
IContract Parent { get; set; }
}

// Contract & ContractLine class:
public class Contract<T: IContract where T : IContractLine, new()
{
public int ContractTestProperty;
protected BindingList<T_lines = new BindingList<T>();
public Contract() { }

public void AddLine()
{
T contractLine = new T();
contractLine.Parent = this;
_lines.Add(contractLine);
}

public BindingList<IContractLineGetLines()
{
return new BindingList<IContractLine>();
}
}

public class ContractLine<T: IContractLine where T : IContractLine,
new()
{
protected IContract _parent;
public IContract Parent
{
get { return _parent; }
set { _parent = value; }
}

public ContractLine() { }
}

// LeaseContract & LeaseContractLine class:
public class LeaseContract : Contract<LeaseContractLine>
{
public int LeaseTestProperty;
public LeaseContract() { }
}

public class LeaseContractLine : ContractLine<LeaseContractLine>
{
public LeaseContractLine() { }
}
}

Thanks,
Lars

Jul 24 '06 #13

P: n/a
Adam,
I am sorry to bombard you, but I came accross a third issue
that seems to put me back on square one:

In the Contract class, I have GetLines which is defined
using <Tto work right:
public BindingList<TGetLines()
{
return _lines;
}

But how do you define this method in the IContract interface??
(you can obviously not use <Tthere...).

Thanks,
Lars

Ps. My sample in the last post uses
"BindingList<IContractLineGetLines()",
but that obviously does not work since I need
generic behavior in the BindingList.


Jul 25 '06 #14

P: n/a
"Lars" <no*****@dev.nullwrote:
I have a base class holding a generic list that needs
to be accessed by both the base class and its subclasses.
What is the best solution to this?
Looking at your source code, I see you basically need two hierarchies:

1) for your container, e.g. LeaseContract <: Contract
2) for your lines, e.g. LeaseContractLine <: ContractLine

.... and when you inherit from your base contract, you want to now be
able to add new lines. Well, the first thing is - you can't do that
trivially, as you already know. If Contract had any kind of list that
allowed adding arbitrary ContractLine descendants, then you could get
contract lines from other branches of the tree - say,
"MurderContractLine" - that don't belong in a LeaseContract. This is the
basic covariance problem.

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

b) Next, in what way would a Contract be a subtype of a LeaseContract
when the two don't support adding the same kinds of lines? This is an
instance of the covariance problem. (Adding a Cat to a List<Dogheld in
a variable typed List<Mammal>). This gives rise to the second question:
what kind of polymorphism / subtyping do you need for Contract
descendants? Would an IContract interface be sufficient?

Can you answer these two questions first? Just to be clear on the
subtyping relationship you need for the two parallel hierarchies
involved.

-- Barry

--
http://barrkel.blogspot.com/
Jul 25 '06 #15

P: n/a
>I have a base class holding a generic list that needs
>to be accessed by both the base class and its subclasses.
What is the best solution to this?

Looking at your source code, I see you basically need two hierarchies:
1) for your container, e.g. LeaseContract <: Contract
2) for your lines, e.g. LeaseContractLine <: ContractLine

a) Now, a question you've got to answer: do you still need to be able to
add arbitrary ContractLine instances to a LeaseContract?
No. If I create a LeaseContract, I would only add LeaseContractLines
to it.
b) Next, in what way would a Contract be a subtype of a LeaseContract
when the two don't support adding the same kinds of lines? This is an
instance of the covariance problem. (Adding a Cat to a List<Dogheld in
a variable typed List<Mammal>). This gives rise to the second question:
what kind of polymorphism / subtyping do you need for Contract
descendants? Would an IContract interface be sufficient?
A LeaseContract would need to inherit the methods, properties, etc. of
a Contract. It would be like any regular subclassing where you can
reuse the functionality of the base class. Some applications use
Contract classes and some use LeaseContract classes.

An application that uses a Contract class or a LeaseContract class
will need to be able to create a reference to a Contract or
LeaseContract or collections of Contracts or LeaseContracts,
as well as retrieving their ContractLines or LeaseContractLines as
BindingList<T(for grids). The application that uses the
ContractClass, etc. also needs to be able to create new
ContractLines or LeaseContractLines to be added to the Contract, or
LeaseContract.

Thanks for your help,
Lars
Jul 25 '06 #16

P: n/a
"Lars" <no****@dev.nullwrote:
a) Now, a question you've got to answer: do you still need to be able to
add arbitrary ContractLine instances to a LeaseContract?

No. If I create a LeaseContract, I would only add LeaseContractLines
to it.
b) Next, in what way would a Contract be a subtype of a LeaseContract
when the two don't support adding the same kinds of lines? This is an
instance of the covariance problem. (Adding a Cat to a List<Dogheld in
a variable typed List<Mammal>). This gives rise to the second question:
what kind of polymorphism / subtyping do you need for Contract
descendants? Would an IContract interface be sufficient?

A LeaseContract would need to inherit the methods, properties, etc. of
a Contract. It would be like any regular subclassing where you can
reuse the functionality of the base class. Some applications use
Contract classes and some use LeaseContract classes.

An application that uses a Contract class or a LeaseContract class
will need to be able to create a reference to a Contract or
LeaseContract or collections of Contracts or LeaseContracts,
as well as retrieving their ContractLines or LeaseContractLines as
BindingList<T(for grids). The application that uses the
ContractClass, etc. also needs to be able to create new
ContractLines or LeaseContractLines to be added to the Contract, or
LeaseContract.
OK. As far as I understand it, you need to have a Contract which
supports databinding via your GetLines(), using BindingList<T>.
(Disclaimer: I don't know much about BindingList<T>.)

So, it seems to me, there is a little trouble in these requirements
because of the mixing of dynamic polymorphism (inheritance) and
parametric polymorphism (generics). It's best to make GetLines()
abstract and have it return an object, so that the correct generic
instance can be returned in descendants. This is only one of the many
reasons DataGridView.DataSource is an Object, I'm guessing.

So, to get polymorphic behaviour with classes, we need an abstract base
class - I'll call it Contract.

In order to support the strongly-typed lines via generics, we'll need a
second base class, called Contract<T>.

Finally, I'll assume that you require your lines to have a
back-reference to the contract. With those constraints in mind, here's
one possible solution:

---8<---
abstract class Contract
{
public abstract object BindingList { get; }
}

abstract class Contract<TLine: Contract
where TLine : ContractLine<TLine>
{
private BindingList<TLine_list;

public override object BindingList { get { return _list; } }

// Whenever needed, you can assign to TLine.Contract because
// it's guaranteed to derive from ContractLine<TLine>
}

abstract class ContractLine<TLine>
where TLine : ContractLine<TLine>
{
public Contract<TLineContract { get {...} internal set {...} }
}
--->8---

-- Barry

--
http://barrkel.blogspot.com/
Jul 25 '06 #17

P: n/a
OK. As far as I understand it, you need to have a Contract which
supports databinding via your GetLines(), using BindingList<T>.
(Disclaimer: I don't know much about BindingList<T>.)

So, it seems to me, there is a little trouble in these requirements
because of the mixing of dynamic polymorphism (inheritance) and
parametric polymorphism (generics). It's best to make GetLines()
abstract and have it return an object, so that the correct generic
instance can be returned in descendants. This is only one of the many
reasons DataGridView.DataSource is an Object, I'm guessing.

So, to get polymorphic behaviour with classes, we need an abstract base
class - I'll call it Contract.

In order to support the strongly-typed lines via generics, we'll need a
second base class, called Contract<T>.

Finally, I'll assume that you require your lines to have a
back-reference to the contract. With those constraints in mind, here's
one possible solution:
Thanks for your advice Barry. At a first glance, I see possible problems
returning an object instead of a BindingList in GetLines. AddLine also
has the same problem, and making it accept anything of type Object
would throw type safety out the window.

Anyhow, I have decided that generics are not suitable for the purpose,
having spent days banging my head (and others') on this. There
probably is a way to make it work, but it is doubtful whether it is
worth the added complexity.

Instead I am going to refactor back to use a non-generic ArrayList
or similar for the lines in the Contract class and write wrapper classes
for type-safety (the "old" .NET 1.1-way).

I'll probably visit this topic again at some point - I would hate
to throw generics out the window permanently, since they do provide
some really neat benefits.

Thanks again,
Lars



Jul 25 '06 #18

P: n/a
"Lars" <no****@dev.nullwrote:
Thanks for your advice Barry. At a first glance, I see possible problems
returning an object instead of a BindingList in GetLines.
That property is strictly for the DataSource in the WinForms control.
You can return a strongly-typed version in a different method / property
in the Contract<TLineclass.

The idea between having two classes, Contract and Contract<TLine>, is
that you unify all the generic descendants under one hierarchy so that
you have appropriate places for runtime polymorphism (Contract) and
strongly-typed generic stuff (Contract<TLine>).
AddLine also
has the same problem, and making it accept anything of type Object
would throw type safety out the window.
Can you afford the time to talk further about this? AddLine() would work
perfectly well in this scenario in the Contract<TLineclass, where you
add a 'new()' constraint to TLine in both Contract<TLineand
ContractLine<TLine>.
Anyhow, I have decided that generics are not suitable for the purpose,
having spent days banging my head (and others') on this. There
probably is a way to make it work, but it is doubtful whether it is
worth the added complexity.
No worries.
I'll probably visit this topic again at some point - I would hate
to throw generics out the window permanently, since they do provide
some really neat benefits.
Sure. I just think you might be giving up too soon :)

-- Barry

--
http://barrkel.blogspot.com/
Jul 25 '06 #19

P: n/a
>Thanks for your advice Barry. At a first glance, I see possible problems
>returning an object instead of a BindingList in GetLines.

That property is strictly for the DataSource in the WinForms control.
You can return a strongly-typed version in a different method / property
in the Contract<TLineclass.
Ok, that should work.
>AddLine also
has the same problem, and making it accept anything of type Object
would throw type safety out the window.

Can you afford the time to talk further about this? AddLine() would work
perfectly well in this scenario in the Contract<TLineclass, where you
add a 'new()' constraint to TLine in both Contract<TLineand
ContractLine<TLine>.
Yes, at a second look, you are absolutely right.
Sure. I just think you might be giving up too soon :)
I just cannot afford to hold up the project I am working on any longer,
so I have now refactored it to use a ContractLineCollection class
that inherits from CollectionBase, instead, and all works beautifully.

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:

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

namespace GenericInheritanceTest5
{
public partial class Form5 : Form
{
public Form5()
{
InitializeComponent();

LeaseContract contract = new LeaseContract();
contract.AddLine(new LeaseContractLine());
contract.AddLine(new LeaseContractLine());
uxDataGrid.DataSource = contract.BindingList2;
}
}

// Is ContractBase really needed??
public abstract class ContractBase
{
public abstract object BindingList { get; }
}

public abstract class ContractBase<TLine: ContractBase
where TLine : ContractLineBase<TLine>, new()
{
private BindingList<TLine_lines = new BindingList<TLine>();
public override object BindingList { get { return _lines; } }
// BindingList2 seems to work as well as BindingList:
public BindingList<TLineBindingList2 { get { return _lines; } }

public void AddLine(TLine line)
{
line.Parent = this;
_lines.Add(line);
}
}

public abstract class ContractLineBase<TLine>
where TLine : ContractLineBase<TLine>, new()
{
public ContractBase<TLineParent; // public for brevity.
}

// Contract & ContractLine class:
public class Contract : ContractBase<ContractLine>
{
public Contract() { }
}

public class ContractLine : ContractLineBase<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() { }
}
}

Note that LeaseContract needs to inherit from Contract, 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.

Also, I don't quite see why "public abstract class ContractBase"
is needed. I have added a "BindingList2" above to demonstrate
(you can bind either BindingList or BindingList2 to the
grid with no apparent difference in the result).

Thanks,
Lars
Jul 25 '06 #20

P: n/a
"Lars" <no****@dev.nullwrote:
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<LeaseContract>.
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

--
http://barrkel.blogspot.com/
Jul 26 '06 #21

P: n/a
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>
in the ContractBase<TLineclass will be of type LeaseContractLine
and not of the subclass-line type.
>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
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

Jul 26 '06 #22

P: n/a
"Lars" <no****@dev.nullwrote:
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>
in the ContractBase<TLineclass 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?
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.
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
class that's generic in this design - i.e. ContractBase<>, yes?
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

--
http://barrkel.blogspot.com/
Jul 26 '06 #23

P: n/a
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>
in the ContractBase<TLineclass 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?
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);
}
}
}

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.

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.
>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
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
Jul 26 '06 #24

P: n/a
"Lars" <no****@dev.nullwrote:
"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.
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<TLineshould
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

--
http://barrkel.blogspot.com/
Jul 27 '06 #25

P: n/a
Ok.

Thanks for your help.
Lars
Jul 27 '06 #26

This discussion thread is closed

Replies have been disabled for this discussion.