我想为一个简单的项目构建一个生产者和消费者的通用系统。
我现在所拥有的是
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。这是一个方差问题吗?这有什么错呢?
发布于 2014-12-17 02:08:25
这是个棘手的案子。你可以使用协方差/逆方差,但是...
我将稍微简化一下代码。为了关闭编译器,你可以这样做:
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将完成另一个答案中建议的技巧,但它从根本上不能解决任何问题。让我来展示一下,现在你想让你的界面:
public interface IConsumer<out IMessage>
{
object Process (IMessage m);
}aaaand,它不会编译。因为简单地说,如果您说out IMessage,这意味着返回类型应该从IMessage派生,而不是参数类型。
所以你可以这样做:
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>,因为如果它遵循以下代码将是可能的:
list[0].Process (new Message2 ());而且这是不可能的,因为Mop1只接受Message1。
所以我来回答这个问题。使用消息分派和纯C#编译器特性实际上不能做任何有意义的事情。我知道这是怎么回事了,你想要静态输入消息之类的东西。不幸的是,你不能在泛型列表中有一个消费特定消息的消费者列表,你必须有像bool CanProcess(IMessage); IMessage Process(IMessage);这样的抽象签名,并在里面进行强制转换。您也可以使用自定义属性和反射来做得稍好一些,但同样,不仅仅是使用C#编译器。
发布于 2014-12-17 01:44:54
如果您计划使用这些接口实现一个典型的生产者、消费者模式,请首先查看this answer。
如果希望IConsumer<TMessage>也是IConsumer<TMessageBase>,其中TMessageBase是TMessage继承或实现的任何类型,则需要使泛型参数协变。使用out修改器。
public interface IConsumer<out T> { }现在可以将IConsumer<Message1>赋值给IConsumer<IMessage>,因为Message1实现了IMessage。
https://stackoverflow.com/questions/27510643
复制相似问题