我有一个通用的接口层次结构,它描述了其他泛型类型的一些控制器,我很难在脑海中澄清为什么特定的转换场景无效。
简化代码如下;
// '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'。但不是两者都在一起......?
答案 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
作为object
。 object
转换是可以的,因为您可以毫不费力地将这些项目用作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