I am having a really bad time with this one. For some reason, I can't
seem to remember how to implement this (elegantly) in c# ... so I
thought I would ask the community.
Basically, I have defined a generic.... and I want to expose a virtual
method on the generic that returns the same generic.
... ignoring the obvious stupidity below, what I am missing that would
allow for something like this? I am purposefully dumbing down my
example for the sake of brevity....
Also, the actual goal/purpose of the code isn't the issue here, it is
more of a language feature question. The example code below is just
that...... example code.
public class TestGeneric<T>
{
List<T> _mylist = new List<T>;
public virtual TestGeneric<T> CutInHalf()
{
// cut the list in half and return it to the caller
}
public virtual Add(T item)
{
_mylist.Add(item);
}
}
public class TestClass
{
public string myGUID = System.Guid.NewGuid().ToString();
}
public class TestClassHeap : TestGeneric<TestClass>
{
}
.... all the code above will compile nicely... now if I actually try
to use it somewhere, i will get type mismatch issues...
TestClassHeap heap = new TestClassHeap();
heap.Add(new TestClass());
heap.Add(new TestClass());
// this line fails to compile because the type returned by
TestGeneric::CutInHalf is different
// than the actual defined type TestClassHeap... which is a
perfectly reasonable interpretation
// by the compiler.
TestClassHeap half = heap.CutInHalf();
What I was hoping to do is return from a method on the generic, and
instance of the DERIVED type (as shown in the usage example above).
I can 'hack it' and get it to work by doing this (ignore the syntactic
issues please, i am doing this on-the-fly... )
public class TestGeneric<T, CT> where CT: new()
{
List<T> _mylist = new List<T>;
public virtual CT CutInHalf()
{
// cut the list in half and return it to the caller
}
public virtual Add(T item)
{
_mylist.Add(item);
}
}
public class TestClass
{
public string myGUID = System.Guid.NewGuid().ToString();
}
public class TestClassHeap : TestGeneric<TestClass, TestClassHeap>
{
}
The definition above does what I want, but it is somewhat hackish, as
it requires me to pass in the class i am defining as a generic
parameter to the generic that the defined class is deriving from ...
Like I said, the hack works, but I was hoping for a more elegant
solution.... and my brain is deadlocked.
:)
Thanks in advance!
Peter Duniho - 20 Mar 2008 20:10 GMT
> [...]
> // this line fails to compile because the type returned by
[quoted text clipped - 10 lines]
> Like I said, the hack works, but I was hoping for a more elegant
> solution.... and my brain is deadlocked.
I guess that depends on your definition of "elegant". :)
The basic issue here is that the generic class, used as a base class in
your example, is only able to create a class instance it knows about. As
you saw, if you tell it about the derived class, all is fine. But
otherwise, it can only return an instance of itself, which of course is
not assignable or even castable to the derived class.
One solution would be to take advantage of the fact that the method is
virtual, and reimplement it in your derived class. In that class
obviously you would know to return the derived class:
class TestClassHeap : TestGeneric<TestClass>
{
public override TestGeneric<TestClass> CutInHalf()
{
// etc.
}
}
You could either reimplement the method entirely, or you could call the
base class and have it do whatever it normally would do, then take the
results and convert them into the derived class. The former is IMHO less
maintainable but would be more efficient. The latter is going to wind up
creating an instance of TestGeneric<TestClass> that you don't actually
return, but rather copy (if you can) members into an instance of
TestClassHeap for use there, and in doing so taking advantage of the base
implementation. There's extra overhead, but it means that you don't have
to copy and paste logic.
If you have members that are private to the base class, then you'll need
to provide some sort of protected helper method that can be called from a
derived class to handle the copying for you.
Another solution might involve reflection, but that's potentially going to
be a noticeable performance problem and I'm not convinced that you'd save
enough performance-wise by avoiding creating the base class instance to
justify the cost of reflection.
In the end, I'm left thinking that what you've already come up with is a
"reasonably" elegant solution. I mean, I would agree if you felt that
making the derived class a type parameter for the generic class is kind of
awkward. But it's not like the other possible solutions don't have their
own awkward aspects either.
One possible improvement might be to make the method itself generic. That
way only callers to the method need to specify the type:
class TestGeneric<T>
{
public virtual U CutInHalf<U>() where U : new()
{
// etc.
}
}
Then you call it:
TestClassHeap half = heap.CutInHalf<TestClassHeap>();
I hope that helps. :)
Pete