为何无法实现受接口约束的泛型类型转换?
Message<ShutdownCommand>不能转换为Message<ICommand>? 这问题其实戳中了C#泛型里一个很关键的点——泛型默认是“不变”的,哪怕你的ShutdownCommand确实继承自ICommand,对应的泛型实例也不会自动有可转换关系。我给你拆解开讲:
首先搞清楚什么是“不变”:简单说就是,
Message<T>这个类的实例之间,不会因为T的继承关系而产生可转换性。举个更常见的例子:List<string>不能转换成List<object>,哪怕string是object的子类——你的问题本质和这个例子完全一样。为什么编译器要这么严格?为了类型安全!假设允许
Message<ShutdownCommand>转成Message<ICommand>,那如果Message<ICommand>里有个方法比如void SetCommand(ICommand cmd),你是不是可以调用这个方法传入一个OtherCommand(另一个实现ICommand的类)?但你的Message<ShutdownCommand>实际上只能存储ShutdownCommand类型的对象,这就会导致类型不匹配的错误,编译器从根源上杜绝了这种风险。你提到的解决思路是对的:要么给
Message<T>定义一个非泛型接口(比如IMessage),让Send()方法接收这个接口类型;要么把Message<T>改成支持协变的接口(注意只有接口和委托能支持协变/逆变)——比如定义public interface IMessage<out T>,这里的out关键字表示T是“输出型”的,只能作为方法返回值,不能作为方法参数。这样IMessage<ShutdownCommand>就能安全地转换成IMessage<ICommand>了,因为协变保证了你只能从接口里读取ICommand,不能往里写入其他类型的命令,不会破坏类型安全。
内容的提问来源于stack exchange,提问作者juliuszelsner

