Mar*_*tus 6 c# generics interface variance
我想为一个简单的项目构建一个生产者和消费者的通用系统.
我现在拥有的是什么
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
}
}
Run Code Online (Sandbox Code Playgroud)
最后一行给出了一个错误 cannot convert from 'Mop1' to '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
}
}
Run Code Online (Sandbox Code Playgroud)
out IMessage将按照另一个答案的建议行事,但它从根本上解决了任何问题.让我说,现在你想要建立你的界面:
public interface IConsumer<out IMessage>
{
object Process (IMessage m);
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
使其编译并有效.但是现在你的list.Add(mop);意志不行.这是理所当然的,因为你Mop1确实不能施展,IConsumer<IMessage>因为如果它是以下代码是可能的:
list[0].Process (new Message2 ());
Run Code Online (Sandbox Code Playgroud)
这是不可能的,因为Mop1只接受Message1.
所以回答这个问题.使用消息调度和纯C#编译器功能,您无法真正做任何有意义的事情.我知道它要去哪里,你想要用消息和东西进行静态输入.遗憾的是,您不能拥有使用通用列表中的特定消息的消费者列表,您必须具有类似的bool CanProcess(IMessage); IMessage Process(IMessage);内部抽象签名和强制转换.您也可以使用自定义属性和反射稍微好一点,但同样,不仅仅是使用C#编译器.
如果您计划使用这些接口实现典型的生产者、消费者模式,请首先查看此答案。
如果您希望 an也IConsumer<TMessage>成为an ,其中是任何继承或实现的类型,那么您需要使泛型参数协变。使用修饰符。IConsumer<TMessageBase>TMessageBaseTMessageout
public interface IConsumer<out T> { }
Run Code Online (Sandbox Code Playgroud)
现在IConsumer<Message1>可分配给IConsumer<IMessage>,因为Message1实现了IMessage。
| 归档时间: |
|
| 查看次数: |
543 次 |
| 最近记录: |