Reputation: 3092
I've got an instance of class B
, which is a specialisation of an abstract class A<TInput, TOutput>
. There are several variants of class B
, as I've implemented it with a variety of input and outputs.
TInput
and TOutput
are constrained to specific input and output classes, let's call them I
and O
.
I am instantiating B
by using Activator.CreateInstance, but since it returns an object, I need to to cast it to A<I, O>
. I expect this to work as I
and O
are base classes (in this case B<SpecialisationOfI, SpecalisationOfO>
).
This is where it fails as this cast is apparently invalid.
Pseudo code:
abstract class I { }
abstract class O { }
abstract class A<TInput, TOutput>
where TInput : I
where TOutput : O
{
abstract TOutput Foo(TInput bar);
}
class Input : I { }
class Output : O { }
class B : A<Input, Output> { }
A<I, O> instance = (A<I, O>)Activator.CreateInstance(typeOfB); // <-- fail
instance.Foo(input);
Is it possible to make this work? Thank you!
edit Based on the answer that I was given, I've solved this by restructuring the code significantly based on the covariance: I moved Foo
from A
to an interface:
interface IA<TResult> {
TResult Foo(object input);
}
class A<TInput, TOutput> : IA<TOutput>
where TInput : I
where TOutput : O {
public TOutput Foo(object input) {
if (!(input is TInput)) {
throw new ArgumentException("input");
}
return FooImpl(input as TInput);
}
protected abstract TOutput FooImpl(TInput input);
}
var instance = (IA<Output>) Activator.CreateInstance(type);
instance.Foo(input);
Thank you for sharing your insight with me!
Upvotes: 0
Views: 210
Reputation: 1500075
This has nothing to do with how you're creating the instance - it's a problem with generic variance. What you're essentially trying to do is similar to this:
List<object> objects = new List<string>();
And the reason it's not valid is that the next line of code might (in general) be:
objects.Add(new object());
If that's trying to add to a list which is really of type List<string>
, that's bad news.
Now if you change from an abstract class to an interface, and if you're using C# 4, you can use generic covariance and generic contravariance:
interface class A<in TInput, out TOutput>
where TInput : I
where TOutput : O
{
abstract TOutput Foo(TInput bar);
}
That's still not going to work for your code though, as you're trying to use both input and output covariantly. Your B
class will have a method like this:
Output Foo(Input bar);
... in other words, it needs input of type Input
. But if you've got an A<I, O>
that should be able to work for any implementation of I
:
A<I, O> x = new B(); // Invalid, as discussed, to prevent...
O output = x.Foo(new SomeOtherI()); // ... this line
Upvotes: 3