Passing derived object of generic class to another class

M

MMAS

Hey everyone --

Curious about some strange behaviour I'm seeing that seems to be
related to my lack of understanding on how generics work in C#. Here's
some simplified code (sorry for strange formatting) to show what my
issue is.

I have a generic abstract class here:

public abstract class MyGenericAbstractClass<T> where T : MyInterface
{
//...whatever...
T _internal_interface;
public MyGenericAbstractClass(T internal_var)
{
_internal_interface = internal_var;
}
}

and I have classes that inherit from it, such as

public class MyDerived :
MyGenericAbstractClass<SomethingThatImplementsMyInterface>
{
///...whatever...
public MyDerived(SomethingThatImplementsMyInterface internal_var) :
base(internal_var) { }
}

My problem comes when I try to pass my "MyDerived" objects to another
class, defined as such:

public class MyBrokenClass
{
// here I have a list of MyGenericAbstractClass objects
private
System.Collections.Generic.LinkedList<MyGenericAbstractClass<MyInterface>>
_list_of_objects;

public void
AddMyGenericAbstractClassToPrivateLinkedList(MyGenericAbstractClass<MyInterface>
object_to_add)
{
_list_of_objects.AddLast(object_to_add);
}
}

All of this compiles just fine.

When I try to do something like this though:
SomethingThatImplementsMyInterface my_something = new
SomethingThatImplementsMyInterface();

MyDerived my_derived = new MyDerived(my_something);
MyBrokenClass my_broken = new MyBrokenClass();
my_broken.AddMyGenericAbstractClassToPrivateLinkedList(my_derived);

Then I get a compile time error as follows:


Error 1:
The best overloaded method match for
'MyBrokenClass.AddMyGenericAbstractClassToPrivateLinkedList(MyGenericAbstractClass<MyInterface>)'
has some invalid arguments

Error 2:
Argument '1': cannot convert from 'MyDerived' to
'MyGenericAbstractClass<MyInterface>'


So, what's the story? What have I done wrong?

Thanks kindly.
Mustafa
 
M

MMAS

[...]
Error 2:
   Argument '1': cannot convert from 'MyDerived' to
'MyGenericAbstractClass<MyInterface>'
So, what's the story? What have I done wrong?

The short answer: generics aren't covariant.  That is, the type parameters  
have to match exactly...just having the parameters related by inheritance 
isn't sufficient.

Off the top of my head, I think that if you inherited  
MyGenericAbstractClass<MyInterface> instead of  
MyGenericAbstractClass<SomethingThatImplementsMyInterface> for MyDerived, 
that would work.  At least, that is, for the specific line you're having  
trouble with (I can't say whether that would fit into the design you're  
trying to do).

Other solutions involving changing the way you relate the types and  
instances could work as well, but without knowing more about the bigger  
picture, it's hard to say.

Pete
 
M

MMAS

Ah, I see. Kinda a drag -- I suppose MyGenericAbstract doesn't have to
be a Generic and I can just cast it's private Interface variable to
the real type when deriving from it. It just doesn't seem as nice that
way...

Thanks for the information.

[...]
Error 2:
   Argument '1': cannot convert from 'MyDerived' to
'MyGenericAbstractClass<MyInterface>'
So, what's the story? What have I done wrong?

The short answer: generics aren't covariant.  That is, the type parameters  
have to match exactly...just having the parameters related by inheritance 
isn't sufficient.

Off the top of my head, I think that if you inherited  
MyGenericAbstractClass<MyInterface> instead of  
MyGenericAbstractClass<SomethingThatImplementsMyInterface> for MyDerived, 
that would work.  At least, that is, for the specific line you're having  
trouble with (I can't say whether that would fit into the design you're  
trying to do).

Other solutions involving changing the way you relate the types and  
instances could work as well, but without knowing more about the bigger  
picture, it's hard to say.

Pete
 
M

MMAS

Hi again Peter -

I'm beginning to think that I do not need or even want generics in my
situation. My List<MyGenericAbstractClass<MyInterface>> should really
become a List<MyGenericAbstractClass> and MyGenericAbstractClass
should just have the _internal_interface typed as MyInterface.

I'm new to Generics and thought this was a situation I could use them
to get some code-time benefits. For example, I can get strongly typed
_internal_interface objects when I was coding my MyDerived objects.
The MyBrokenClass example, in the real code, is really a wrapper
around a collection of MyGenericAbstractClass objects that will be
calling a common method on all objects.

To get more detailed: my MyGenericAbstractClass in my real code is an
abstract "Step". The MyInterface class in my real code is the specific
arguments (StepArguments) for that step, and the MyBrokenClass is the
"StepManager" that calls the "RunStep" method for each Step object.
Using Generics seemed a good idea to ensure that each specific
implemented step got the correct type of Arguments and also so that I
can use those arguments while coding the Step without having to do any
casting. It seems that I was wrong.

I can gladly post more code if you'd like to see how I've really
implemented all of this, but figured a short explanation would be
easier than many lines of code.

Thanks again for the help.
Mustafa
 
M

MMAS

I've seen the folly of my ways, as they say. I've rid myself of the
Arguments class and moved the various arguments directly into each
derived Step class. I had a "validate" method on the Arguments
interface that I was calling before each step object was run by my
StepManager (to make sure all arguments were valid for this object),
but have now made this an abstract function in my abstract base "Step"
class that is implemented in each derived class.

Like many first attempts at using something "new", I think I was using
Generics just to use them rather than using them for the right
reasons.
 

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