我想为一个简单的项目构建一个生产者和消费者的通用系统。
我现在拥有的是
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。这是一些方差问题吗?这有什么问题?
最佳答案
这是一个棘手的案例。您可以使用协变/逆变,但是...
我会稍微简化代码。只是为了关闭编译器,你可以这样做:
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);
}
aaa 它不会编译。因为简单地说,如果你说 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# 编译器。
关于c# - 通用列表的接口(interface)继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27510643/