首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >协同/逆变接口和可分配性

协同/逆变接口和可分配性
EN

Stack Overflow用户
提问于 2013-01-15 06:58:04
回答 2查看 105关注 0票数 2

我有一些问题,我想,方差,我不完全理解。我有一个带有两个类型参数的泛型接口,如下所示:

代码语言:javascript
复制
public interface IInvoker<TParameter, TResult> {
    TResult Invoke(TParameter parameter);
}

现在,在我的例子中,我想让TATB成为抽象类,如下所示:

代码语言:javascript
复制
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<,>实现,所以我想我可以这样做

代码语言:javascript
复制
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>)上添加一些inout了,但这并没有帮助。

但我不明白为什么?据我所知,任何使用IInvoker<AbstractParameter, AbstractResult>的人都应该能够调用Invoke,对吧?我遗漏了什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-01-15 07:16:09

问题是TResult类型参数是逆变的,但您试图在赋值中同时使用它们,例如

代码语言:javascript
复制
IInvoker<AbstractParameter, AbstractResult> i1 = new InvokerOne();

TResult是协变的,所以AbstractResult是一个比Result1更大的类型是可以的。但是,由于TParameter是反向变量,因此TParameter必须是比Parameter1更小的类型,而AbstractParameter并非如此。

如果上述条件有效,您可以执行以下操作:

代码语言:javascript
复制
class OtherParameter : AbstractParameter { ... };
IInvoker<AbstractParameter, AbstractResult> i1 = new InvokerOne();
i1.Invoke(new OtherParameter());

这并不安全。

但是,您可以拥有以下内容:

代码语言:javascript
复制
public class OtherParameter1 : Parameter1 { }
IInvoker<OtherParameter1, AbstractResult> i1 = new InvokerOne();

在这里,OtherParameter1可以作为参数传递给Invoke,因为它始终是有效的for Parameter1

票数 5
EN

Stack Overflow用户

发布于 2013-01-15 08:44:00

你遗漏的一件事就是你的接口中的方差声明。该接口不是变体,除非您将其声明为:

代码语言:javascript
复制
public interface IInvoker<in TParameter, out TResult>
//                        ^^             ^^^
//                        Look!          Here too!
{
    TResult Invoke(TParameter parameter);
}

inout关键字有助于强调差异的性质。该类型相对于in参数是逆变的,相对于out参数是协变的。换句话说,假设使用常见的Animal : Mammal : Cat示例,您可以做到这一点:

代码语言:javascript
复制
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解释的那样,如果你可以做你想做的事情,你就会遇到一些麻烦,因为没有什么可以阻止这一点:

代码语言:javascript
复制
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 */) };

解决这个问题的一种方法是从接口中删除参数。将其设置为调用者的私有字段,或者创建一个将调用者与其参数配对的类,如下所示:

代码语言:javascript
复制
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方法无关的处理,那么您的调用程序应该实现一些其他公共接口或继承一些其他公共基类,如下所示:

代码语言:javascript
复制
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() };

或者这样:

代码语言:javascript
复制
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() };
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14328351

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档