首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >泛型列表的接口继承

泛型列表的接口继承
EN

Stack Overflow用户
提问于 2014-12-17 01:39:47
回答 2查看 834关注 0票数 6

我想为一个简单的项目构建一个生产者和消费者的通用系统。

我现在所拥有的是

代码语言:javascript
复制
public interface IMessage     {    }
public interface Message1 : IMessage    {    }
public interface Message2 : IMessage    {    }
public interface IConsumer<T>    {    }
public interface IProducer<T>    {    }

public class Mop1 : IConsumer<Message1>, IProducer<Message2>
{
}



class Program
{
    static void Main(string[] args)
    {
        var list = new List<IConsumer<IMessage>>();
        var mop = new Mop1();
        list.Add(mop);   // Error occurs here
    }
}

最后一行给出了类似于cannot convert from 'Mop1' to 'IConsumer<GenericPubSub.IMessage>'的错误

但是Mop1实现了IMessage派生类型的IConsumer。这是一个方差问题吗?这有什么错呢?

EN

回答 2

Stack Overflow用户

发布于 2014-12-17 02:08:25

这是个棘手的案子。你可以使用协方差/逆方差,但是...

我将稍微简化一下代码。为了关闭编译器,你可以这样做:

代码语言:javascript
复制
public interface IMessage { }

public interface Message1 : IMessage { }


public class Mop1 : IConsumer<Message1>
{
}

public interface IConsumer<out IMessage>
{
}


class Program
{
    static void Main(string[] args)
    {
        var list = new List<IConsumer<IMessage>>();
        var mop = new Mop1();

        list.Add(mop);   // Error occurs here
    }
}

out IMessage将完成另一个答案中建议的技巧,但它从根本上不能解决任何问题。让我来展示一下,现在你想让你的界面:

代码语言:javascript
复制
public interface IConsumer<out IMessage>
{
    object Process (IMessage m);
}

aaaand,它不会编译。因为简单地说,如果您说out IMessage,这意味着返回类型应该从IMessage派生,而不是参数类型。

所以你可以这样做:

代码语言:javascript
复制
public interface IConsumer<in IMessage>
{
    object Process (IMessage m);
}

public class Mop1 : IConsumer<Message1>
{
    public object Process (Message1 msg)
    {
        return null;
    }
}

以使其编译并有效。但是现在你的list.Add(mop);不能工作了。这是理所当然的,因为您的Mop1确实不能转换为IConsumer<IMessage>,因为如果它遵循以下代码将是可能的:

代码语言:javascript
复制
list[0].Process (new Message2 ());

而且这是不可能的,因为Mop1只接受Message1

所以我来回答这个问题。使用消息分派和纯C#编译器特性实际上不能做任何有意义的事情。我知道这是怎么回事了,你想要静态输入消息之类的东西。不幸的是,你不能在泛型列表中有一个消费特定消息的消费者列表,你必须有像bool CanProcess(IMessage); IMessage Process(IMessage);这样的抽象签名,并在里面进行强制转换。您也可以使用自定义属性和反射来做得稍好一些,但同样,不仅仅是使用C#编译器。

票数 5
EN

Stack Overflow用户

发布于 2014-12-17 01:44:54

如果您计划使用这些接口实现一个典型的生产者、消费者模式,请首先查看this answer

如果希望IConsumer<TMessage>也是IConsumer<TMessageBase>,其中TMessageBaseTMessage继承或实现的任何类型,则需要使泛型参数协变。使用out修改器。

代码语言:javascript
复制
public interface IConsumer<out T>    {    }

现在可以将IConsumer<Message1>赋值给IConsumer<IMessage>,因为Message1实现了IMessage

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27510643

复制
相关文章

相似问题

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