我有一些问题,我想,方差,我不完全理解。我有一个带有两个类型参数的泛型接口,如下所示:
public interface IInvoker<TParameter, TResult> {
TResult Invoke(TParameter parameter);
}现在,在我的例子中,我想让TA和TB成为抽象类,如下所示:
public abstract class AbstractParameter {
public int A { get; set; }
}
public abstract class AbstractResult {
public string X { get; set; }
}
public class Parameter1 : AbstractParameter {
public int B { get; set; }
}
public class Result1 : AbstractResult {
public string Y { get; set; }
}
// ... Many more types然后,我想要处理一组不同的IInvoker<,>实现,所以我想我可以这样做
public class InvokerOne : IInvoker<Parameter1, Result1> { /* ... */ }
public class InvokerTwo : IInvoker<Parameter2, Result2> { /* ... */ }
// ..
IInvoker<AbstractParameter, AbstractResult>[] invokers = { new InvokerOne(), new InvokerTwo() };这是行不通的,因为据我所知,不能从IInvoker<Parameter1, Result1> (和朋友)那里分配IInvoker<AbstractParameter, AbstractResult>。首先,我认为现在是时候在我的界面(interface IInvoker<in TParameter, out TResult>)上添加一些in和out了,但这并没有帮助。
但我不明白为什么?据我所知,任何使用IInvoker<AbstractParameter, AbstractResult>的人都应该能够调用Invoke,对吧?我遗漏了什么?
发布于 2013-01-15 07:16:09
问题是TResult类型参数是逆变的,但您试图在赋值中同时使用它们,例如
IInvoker<AbstractParameter, AbstractResult> i1 = new InvokerOne();TResult是协变的,所以AbstractResult是一个比Result1更大的类型是可以的。但是,由于TParameter是反向变量,因此TParameter必须是比Parameter1更小的类型,而AbstractParameter并非如此。
如果上述条件有效,您可以执行以下操作:
class OtherParameter : AbstractParameter { ... };
IInvoker<AbstractParameter, AbstractResult> i1 = new InvokerOne();
i1.Invoke(new OtherParameter());这并不安全。
但是,您可以拥有以下内容:
public class OtherParameter1 : Parameter1 { }
IInvoker<OtherParameter1, AbstractResult> i1 = new InvokerOne();在这里,OtherParameter1可以作为参数传递给Invoke,因为它始终是有效的for Parameter1。
发布于 2013-01-15 08:44:00
你遗漏的一件事就是你的接口中的方差声明。该接口不是变体,除非您将其声明为:
public interface IInvoker<in TParameter, out TResult>
// ^^ ^^^
// Look! Here too!
{
TResult Invoke(TParameter parameter);
}in和out关键字有助于强调差异的性质。该类型相对于in参数是逆变的,相对于out参数是协变的。换句话说,假设使用常见的Animal : Mammal : Cat示例,您可以做到这一点:
IInvoker<Mammal, Mammal> a = Whatever();
IInvoker<Cat, Mammal> b = a;
IInvoker<Mammal, Animal> c = a;这本身并不是特别有用,但重点是,您可以在需要IInvoker<Cat, Mammal>或IInvoker<Mammal, Animal>的任何地方使用IInvoker<Mammal, Mammal>。
您的问题中还遗漏了一些重要的东西:您到底想要如何处理您的IInvoker<,>实现集?(“我想处理一组不同的IInvoker<,>实现...”)这个问题的答案将引导您找到解决方案。你想用从AbstractParameter继承的一些对象来调用它们吗?如果是这样,正如Lee解释的那样,如果你可以做你想做的事情,你就会遇到一些麻烦,因为没有什么可以阻止这一点:
IInvoker<AbstractParameter, AbstractResult>[] invokers = { new InvokerOne(), new InvokerTwo() };
AbstractParameter[] parameters = { new ParameterOne(), new ParameterTwo() };
AbstractResult[] results = { invokers[0].Invoke(parameters[1] /* oops */), invokers[1].Invoke(parameters[0] /* oops */) };解决这个问题的一种方法是从接口中删除参数。将其设置为调用者的私有字段,或者创建一个将调用者与其参数配对的类,如下所示:
interface IInvokerParameterPair<out TResult>()
where TResult : AbstractResult
{
TResult InvokeTheInvoker();
}
class InvokerParameterPair<TParameter, TResult> : IInvokerParameterPair<TResult>
where TParameter : AbstractParameter
where TResult : AbstractResult
{
private IInvoker<TParameter, TResult> _invoker;
private TParameter _parameter;
public InvokerParameterPair(IInvoker<TParameter, TResult> invoker, TParameter parameter)
{
_invoker = invoker;
_parameter = parameter;
}
public TResult InvokeTheInvoker()
{
return _invoker.Invoke(_parameter);
}
}另一方面,如果您想做一些与Invoke方法无关的处理,那么您的调用程序应该实现一些其他公共接口或继承一些其他公共基类,如下所示:
public interface IProcessable { }
public interface IInvoker<in TParameter, out TResult> : IProcessable
{
TResult Invoke(TParameter parameter);
}
public class InvokerOne : IInvoker<Parameter1, Result1> { /* ... */ }
public class InvokerTwo : IInvoker<Parameter2, Result2> { /* ... */ }
IProcessable[] invokers = { new InvokerOne(), new InvokerTwo() };或者这样:
public interface IInvoker<in TParameter, out TResult> : IProcessable
{
TResult Invoke(TParameter parameter);
}
public abstract class Processable { }
public class InvokerOne : Processable, IInvoker<Parameter1, Result1> { /* ... */ }
public class InvokerTwo : Processable, IInvoker<Parameter2, Result2> { /* ... */ }
Processable[] invokers = { new InvokerOne(), new InvokerTwo() };https://stackoverflow.com/questions/14328351
复制相似问题