我想为一个简单的项目构建一个生产者和消费者的通用系统.
我现在拥有的是什么
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 } }
最后一行给出了一个错误,例如无法从’Mop1’转换为’IConsumer< GenericpubSub.IMessage>‘
但是Mop1实现了IMessage派生类型的IConsumer.这是一些差异问题吗?这有什么问题?
解决方法
这很棘手.你可以使用co / contravariance,但……
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 } }
IMessage会像另一个答案所建议的那样做,但它从根本上解决了什么问题.让我说,现在你想要建立你的界面:
public interface IConsumer<out IMessage> { object Process (IMessage m); }
aaaand它不会编译.因为简单地说如果你说出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(拖把);不管用.理所当然,因为你的Mop1确实不能转换为IConsumer< IMessage>因为如果它是以下代码将是可能的:
list[0].Process (new Message2 ());
并且它是不可能的,因为Mop1只接受Message1.
所以回答这个问题.使用消息调度和纯C#编译器功能,您无法真正做任何有意义的事情.我知道它要去哪里,你想要用消息和东西进行静态输入.遗憾的是,您不能拥有使用通用列表中特定消息的消费者列表,您必须拥有像bool CanProcess(IMessage)这样的抽象签名; IMessage过程(IMessage);并投入内部.您也可以使用自定义属性和反射稍微好一点,但同样,不仅仅是使用C#编译器.