Suggestions? Returning subclass instances from parent class methods

N

Niels Ull

Hi!

I have a generic abstract base class MyCollection<T> which represents a collection
of T's with some common utility methods.
I then have a number of non-generic subclasses, e.g.

class FooCollection : MyCollection<Foo>
{ /* utility methods only for foo collections */ }


class BarCollection : MyCollection<Bar>
{ /* utility methods only for Bar collections */ }


Now, I'd like to provide a subset method in MyCollection - e.g. for getting
the first 10 items
(In real life selecting the subset is more complex and based on the structure
of MyCollection as well as properties of the T base type)

FooCollection allFoos = ...
FooCollection tenFoos = allFoos.GetSubset(10);

But I'd like to return a FooCollection - not a MyCollection<Foo>. The best
I have been able to come up is this method in MyCollection:


DERIVEDCOLLECTION GetSubset<DERIVEDCOLLECTION>(int count) where DERIVEDCOLLECTION:
MyCollection<T>, new()
{
DERIVEDCOLLECTION result = new DERIVEDCOLLECTION();
for(int i = 0; i < count; i++) // example logic only!
result.Add(this);
return result;
}

This is not very elegant, since it requires the user to specify the target
class:
FooCollection tenFoos = allFoos.GetSubset<FooCollection>(10);


An alternative would be to return an IEnumerable<T>, but that would require
the user to write

FooCollection allFoos = ...
FooCollection tenFoos = new FooCollection();
tenFoos.AddAll(allFoos.GetSubset(10))

Or, finally, I could implement new GetSubset in all derived collections -
but since there are a number of such functions, that's not too useful either.

Does anybody have any suggestions?
 
A

Adam Clauss

One possibility, add a second type to the generic parameters:

class FooCollection : MyCollection<Foo, FooCollection>
{}

where MyCollection is now something like:

class MyCollection<T,S> where S : new()
{
public S GetSubset(int count)
{
S newCollection = new S();
for (int i = 0; i < count; i++)
{
newCollection.Add(this);
}
return newCollection;
}
}

This would then simplify the callers syntax to simply:

FooCollection tenFoos = allFoos.GetSubset(10);

Which was what you wanted correct?

This comes with an assumption: that each of your subclasses has a default
constructor (presumably to create an empty collection).
 
A

Adam Clauss

Realized I forgot one more thing before that will work.
S newCollection = new S();
newCollection.Add(this);


In order to call newCollection.Add(), we need the compiler to know that S
actually derives from MyCollection, more specifically MyCollection of the
same type (T). So change the MyCollection definition to:
class MyCollection<T,S> where S : MyCollection<T,S>, new()

It looks a bit redundant, but it ties all the appropriate things together to
let the compiler know that anything of type S is effectively the same as
itself.

--
Adam Clauss


Adam Clauss said:
One possibility, add a second type to the generic parameters:

class FooCollection : MyCollection<Foo, FooCollection>
{}

where MyCollection is now something like:

class MyCollection<T,S> where S : new()
{
public S GetSubset(int count)
{
S newCollection = new S();
for (int i = 0; i < count; i++)
{
newCollection.Add(this);
}
return newCollection;
}
}

This would then simplify the callers syntax to simply:

FooCollection tenFoos = allFoos.GetSubset(10);

Which was what you wanted correct?

This comes with an assumption: that each of your subclasses has a default
constructor (presumably to create an empty collection).

--
Adam Clauss


Niels Ull said:
Hi!

I have a generic abstract base class MyCollection<T> which represents a
collection of T's with some common utility methods. I then have a number
of non-generic subclasses, e.g.

class FooCollection : MyCollection<Foo>
{ /* utility methods only for foo collections */ }


class BarCollection : MyCollection<Bar>
{ /* utility methods only for Bar collections */ }


Now, I'd like to provide a subset method in MyCollection - e.g. for
getting the first 10 items
(In real life selecting the subset is more complex and based on the
structure of MyCollection as well as properties of the T base type)

FooCollection allFoos = ...
FooCollection tenFoos = allFoos.GetSubset(10);

But I'd like to return a FooCollection - not a MyCollection<Foo>. The
best I have been able to come up is this method in MyCollection:


DERIVEDCOLLECTION GetSubset<DERIVEDCOLLECTION>(int count) where
DERIVEDCOLLECTION: MyCollection<T>, new()
{
DERIVEDCOLLECTION result = new DERIVEDCOLLECTION();
for(int i = 0; i < count; i++) // example logic only!
result.Add(this);
return result;
}

This is not very elegant, since it requires the user to specify the
target class: FooCollection tenFoos =
allFoos.GetSubset<FooCollection>(10);


An alternative would be to return an IEnumerable<T>, but that would
require the user to write

FooCollection allFoos = ...
FooCollection tenFoos = new FooCollection();
tenFoos.AddAll(allFoos.GetSubset(10))

Or, finally, I could implement new GetSubset in all derived collections -
but since there are a number of such functions, that's not too useful
either.

Does anybody have any suggestions?

 

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