通用铸造

Gha*_*han 1 .net c# generics casting

我有这个通用类

public abstract class BaseExportCommand<T> where T : EditableEntity, new()
{
....
}
Run Code Online (Sandbox Code Playgroud)

我有这个派生类

public class MessageExportCommand : BaseExportCommand<Message> 
{
 .....
}
Run Code Online (Sandbox Code Playgroud)

Message从EdittableEntity继承的位置

public class Message : EditableEntity
{
...
}
Run Code Online (Sandbox Code Playgroud)

现在,当我尝试做这个陈述时

BaseExportCommand<EditableEntity> myValue = new MessageExportCommand ();
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

Cannot convert type 'MessageExportCommand' to 'BaseExportCommand<EditableEntity>'   
Run Code Online (Sandbox Code Playgroud)

知道为什么吗?

Jon*_*eet 8

知道为什么吗?

是.您的泛型类型不协变在T.

我们不能立即告诉它是否应该.例如,假设它看起来像这样:

public abstract class BaseExportCommand<T> where T : EditableEntity, new()
{
    public abstract DoSomethingWithEntity(T entity);
}
Run Code Online (Sandbox Code Playgroud)

那么假设你可以写:

BaseExportCommand<EditableEntity> myValue = new MessageExportCommand();
EditableEntity entity = new SomeEditableEntity();
myValue.DoSomethingWithEntity(entity);
Run Code Online (Sandbox Code Playgroud)

......而MessageExportCommand只是期望DoSomethingWithEntity(Message).

它如果你只使用是安全的T为输出从BaseExportCommand<T>只针对接口和委托- ,但不幸的是C#不会让你的类声明协变类型参数.所以,你可以潜在地写:

// Note the "out" part, signifying covariance
public interface IExportCommand<out T> where T : EditableEntity, new()
Run Code Online (Sandbox Code Playgroud)

然后:

IExportCommand<EditableEntity> = new MessageExportCommand();
Run Code Online (Sandbox Code Playgroud)

...但它取决于界面中声明的成员.如果您尝试T在任何"输入"位置使用,编译器将注意并阻止您共同声明T.

有关详细信息,请参阅MSDN中的通用类型中的差异,以及Eric Lippert关于该主题的博客文章(重新安排和放松,有很多内容需要阅读).