关于向下转型的通用继承澄清

时间:2012-05-22 00:17:26

标签: c# generics inheritance

我有一个通用的接口层次结构,它描述了其他泛型类型的一些控制器,我很难在脑海中澄清为什么特定的转换场景无效。

简化代码如下;

// 'DTO' interfaces
public interface IBase
{ }

public interface IDerived : IBase
{ }


// 'DTOs'
public class Base : IBase
{ }

public class Derived : Base, IDerived
{ }


// controller interfaces
public interface IBaseController<T> where T : class, IBase
{ }

public interface IDerivedController : IBaseController<IDerived>
{ }


// controllers
public class BaseController<T> : IBaseController<T>
    where T : class, IBase
{ }

public class DerivedController : BaseController<IDerived>, IDerivedController
{ }

现在,我遇到麻烦的情况是这样的;

IDerivedController x = new DerivedController();
bool is1 = x is IDerivedController;   // true
bool is2 = x is IBaseController<IDerived>; // true
bool is3 = x is IBaseController<IBase>; // false ???

最后一行是我的困惑所在。控制器接口正确关联,以及'DTO'。但不是两者都在一起......?

3 个答案:

答案 0 :(得分:1)

如果您有一组形状,请考虑这意味着什么:Collection<Triagle>与(是-a Collection<Shape>相同?如果它比我们可以将任何形状放入我们的三角形集合中。 OTOH如果Collection<Shape>是(是-a Collection<Triangle>,那么我们可以在其中放置Square(毕竟,它是形状的集合),并期望只获得Triangle

您的BaseController<Iderived>

也是如此

答案 1 :(得分:1)

有关变体通用接口的信息,请参阅http://msdn.microsoft.com/en-us/library/dd997386,这是您所要求的核心。您必须声明IBaseController<IDerived>实际上可以用作IBaseController<IBase>,以便将其用作List<string>

出于这个原因,请考虑尝试使用IList<object>作为IEnumerable<string>与使用IEnumerable<object> IEnumerable作为objectobject转换是可以的,因为您可以毫不费力地将这些项目用作List<string>,但是您无法将任何string添加到public interface IBaseController<out T> where T : class, IBase,因为它会包含除x is IBaseController<IBase> s之外的其他内容。

如果您将其更改为true,则T会变为out。如果编译器抱怨您无法使{{1}}成为{{1}}参数,那么您无法进行您想要进行的转换(除非您更改接口以使其兼容)。< / p>

答案 2 :(得分:1)

这是因为IBaseController不是covariant。你可以通过这样声明来使它变得协变:

public interface IBaseController<out T> where T : class, IBase
{
}

但是T只能在输出位置使用。

想象一下,您的界面如下所示:

public interface IBaseController<T> where T : class, IBase
{
    void DoSomething(T x);
}

IBaseController<IDerived>中,方法签名为:

void Something(IDerived x)

现在,如果IBaseController<IDerived>可分配给IBaseController<IBase>,您可以执行以下操作:

public class Derived2 : Base
{ }


IDerivedController x = ...
IBaseController<IBase> y = x;
y.DoSomething(new Derived2()); // oops! Derived2 doesn't implement IDerived