在消除我知识上的灾难性差距的过程中,我勾画了一个特定的类,像这样(我没有在列表中包括构造函数和方法,无论如何这个问题都很难理解):
public class SomeFractal
{
public int Generation;
public SomeFractal Parent { get; private set; }
public SomeFractal Root
{
get
{
if (Generation == 0) return this;
else return Parent.Root;
}
}
List<Fractal> Children {get;}
}
这样做是为了计算递归算法,但我不是在谈论那个。
出现了描述几个细节不同的“分形”类并实现单个接口的想法,让它成为 IFractal。该界面应包含主要属性(父、根、子分支)。
大多数属性自然会返回描述它们的类的对象(我们有一个分形,还记得吗?),所以如果我希望它们在接口中被描述,它必须是协变的。结果是这样的:
interface IFractal <out T> where T : IFractal<T>
{
T Root { get; }
T Parent { get; }
IEnumerable<T> Children { get; }
}
实际上,没有问题——接口当然是由上述类实现的:
class SomeFractal : IFractal<SomeFractal>
如果我想在其他地方使用类型变量IFractal<T>来从特定的实现类中抽象出来,问题就会出现。毕竟,我必须指定参数 T,它只能是实现了 IFractal 的类——也就是说,没有获得任何抽象,在这种情况下它与使用类本身是一样的。
有没有办法绕过这个限制?也许我在协方差方面失败了?
请不要讨论实际应用的可行性,这纯粹是出于教育目的的理论化。
更新
我将用一个例子来解释非泛型
interface IFractal
{
IFractal Root { get; }
IEnumerable<IFractal> Children { get; }
}
class Fractal : IFractal
{
public IFractal Root { get {
if (parent == null) return this;
else return parent.Root;
} }
public IEnumerable<IFractal> Children { get; }
private Fractal parent;
private string prettyCoolField;
public string GetRootField() {
return Root.prettyCoolField;
}
}
GetRootField()它不会工作,因为它IFractal没有 field prettyCoolField。返回(Fractal)Root.prettyCoolField;也不起作用。还是我错误地转换了类型?
通常他们是这样做的:
这将允许在不知道 T 的具体类型的情况下遍历“导航”属性。
如果你一方面想要一个表示树、树的元素的类,另一方面想要一个不依赖于类本身的读取接口,那么你需要将数据和接口的实现。
这没有问题,因为 getter-only 属性
IEnumerable<T>已经是类型 covariantT。结果,我们得到了通常的界面:及其实施: