I'm not sure if this is possible at all, looking for some clarification.
I have a class structure like this:
public class FooBase
{
//Some base class
}
public class BarBase
{
//Some base class
}
public class Foo : FooBase
{
//Implementation
}
public class Bar : BarBase
{
//Implementation
}
public abstract class FooBarHolderAbstract<T, V> where T: FooBase where V: BarBase
{
}
public class MyFooBarHolderImpl : FooBarHolderAbstract<Foo, Bar>
{
}
public class FooBarTest
{
public void DoSomethingWithFooBar<T>() where T : FooBarHolderAbstract<FooBase, BarBase>
{
//Do something tith the obj
}
public void RunTest()
{
//This doesn't work, compiler says MyFooBarHolder is not convertible to FooBarHolderAbstract<FooBase, BarBase>
DoSomethingWithFooBar<MyFooBarHolderImpl>();
}
}
In the FooBarTest class, I'd like to create a method which accepts a generic parameter, which inherits from the abstract class having two generic parameters. The class MyFooBarHolderImpl extends the abstract base class and specifies its generic parameters with types which are inheriting from the abstract class' generic parameter types.
When I try to call this method (DoSomethingWithFooBar()) the compiler tells me that the type MyFooBarHolderImpl must be convertible to FooBarHolderAbstract
Is this something which cannot be done at all, or am I missing a concept/syntax?
Thanks in advance!
Well, it can't be done directly - a FooBarHolderAbstract<Foo, Bar>
isn't a FooBarHolderAbstract<FooBase, BarBase>
. It's not clear whether or not you could logically have that, because we don't know what's in the abstract class.
You're basically looking for generic covariance, but that isn't supported on classes anyway - so you may want to introduce an interface:
public interface IFooBarHolder<out T, out V>
where T: FooBase
where V: BarBase
{
// Define what you need in here
}
public abstract class FooBarHolderAbstract<T, V> : IFooBarHolder<T, V>
where T : FooBase
where V : BarBase
{
}
At that point, you can change FooBarTest
to:
public void DoSomethingWithFooBar<T>() where T : IFooBarHolder<FooBase, BarBase>
{
//Do something with the obj
}
... because an IFooBarHolder<Foo, Bar>
is an IFooBarHolder<FooBase, BarBase>
.
However, this only works if you can define all your operations for the interface which use T
and V
in "out" positions, e.g. return types from methods. If you ever need them in "input" positions, e.g. as method parameters, you're stuck - because a method expecting a Foo
can't handle any other kind of FooBase
.
See more on this question at Stackoverflow