我有一个类B
的实例,它是抽象类A<TInput, TOutput>
的特化。类B
有几种变体,因为我已经用各种输入和输出实现了它。
TInput
和TOutput
受限于特定的输入和输出类,我们称之为I
和O
。
我使用Activator.CreateInstance实例化B
,但由于它返回了一个对象,我需要将其强制转换为A<I, O>
。我希望这可以用作I
和O
是基类(在本例中为B<SpecialisationOfI, SpecalisationOfO>
)。
这是失败的地方,因为此演员表明显无效。
伪代码:
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);
是否有可能使这项工作?谢谢!
编辑根据我给出的答案,我通过基于协方差重构代码来解决这个问题:我将Foo
从A
移到了接口:
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);
感谢您与我分享您的见解!
答案 0 :(得分:3)
这与您创建实例的方式无关 - 这是generic variance的问题。你基本上要做的就是这样:
List<object> objects = new List<string>();
它无效的原因是下一行代码可能(通常)是:
objects.Add(new object());
如果那是在尝试添加到类型为List<string>
的 的列表中,那就是坏消息。
现在,如果您从抽象类更改为接口,并且如果您使用的是C#4,则可以使用泛型协方差和泛型逆转:
interface class A<in TInput, out TOutput>
where TInput : I
where TOutput : O
{
abstract TOutput Foo(TInput bar);
}
但仍然不适用于您的代码,因为您正在尝试同时使用两个输入和输出。您的B
课程将采用以下方法:
Output Foo(Input bar);
...换句话说,它需要输入Input
类型。但是,如果您有一个A<I, O>
应该能够为 I
的任何实施工作:
A<I, O> x = new B(); // Invalid, as discussed, to prevent...
O output = x.Foo(new SomeOtherI()); // ... this line