下面的代码是实现协变返回类型的唯一方法吗?
public abstract class BaseApplication<T> { public T Employee{ get; set; } } public class Application : BaseApplication<ExistingEmployee> {} public class NewApplication : BaseApplication<NewEmployee> {}
我希望能够构造一个Application或NewApplication,并使其从Employee属性返回适当的Employee类型。
var app = new Application(); var employee = app.Employee; // this should be of type ExistingEmployee
我相信这段代码可以正常工作,但是当我有多个需要相同行为的属性时,它确实很讨厌。
还有其他方法可以实现此行为吗?泛型还是其他?
首先,您的问题的答案是否定的,C#不支持任何形式的虚拟覆盖类型的返回类型协方差。
许多回答者和评论者说“这个问题没有协方差”。这是不正确的。原始海报完全像他们一样提出问题。
回想一下,协变映射是保留了某些其他关系的存在和方向的映射。例如,从类型T到类型的映射IEnumerable<T>是协变的,因为它保留了分配兼容性关系。如果Tiger与Animal分配兼容,则地图下的变换也将保留:IEnumerable<Tiger>分配与兼容IEnumerable<Animal>。
T
IEnumerable<T>
IEnumerable<Tiger>
IEnumerable<Animal>
这里的协变映射很难看,但仍然存在。本质上的问题是:这是否合法?
class B { public virtual Animal M() {...} } class D : B { public override Tiger M() {...} }
老虎与动物兼容。现在,从类型T映射到方法“ public TM()”。 该映射是否保留兼容性 ?也就是说, 如果Tiger出于分配目的与Animal兼容,那么 为了虚拟覆盖而public Tiger M()兼容public Animal M()吗?
public Tiger M()
public Animal M()
C#的答案是“否”。C#不支持这种协方差。
既然我们已经确定问题是使用正确的类型代数术语来提出的,那么对实际问题还有更多的想法。显而易见的第一个问题是,该属性甚至尚未被声明为虚拟属性,因此虚拟兼容性问题尚无定论。显而易见的第二个问题是“获取;设置;设置”;即使C#支持返回类型协方差,property属性也不是协变的,因为 带有setter的属性的类型不仅是其返回类型,而且还是其形式参数类型 。您需要 逆变 正式参数类型,实现类型安全。如果我们允许使用setter的属性返回类型协方差,那么您将:
class B { public virtual Animal Animal{ get; set;} } class D : B { public override Tiger Animal { ... } } B b = new D(); b.Animal = new Giraffe();
嘿,我们刚刚将一只长颈鹿送给了一只期待老虎的饲养员。如果我们支持此功能,则必须将其限制为返回类型(就像我们对通用接口的赋值-兼容性协方差所做的那样。)
第三个问题是CLR不支持这种差异。如果我们想以这种语言来支持它(就像我相信托管C ++那样),那么我们将不得不采取一些合理的措施来解决CLR中的签名匹配限制。
您可以通过仔细定义具有适合其基类类型的适当返回类型的“新”方法来自己采取这些英勇措施:
abstract class B { protected abstract Animal ProtectedM(); public Animal Animal { get { return this.ProtectedM(); } } } class D : B { protected override Animal ProtectedM() { return new Tiger(); } public new Tiger Animal { get { return (Tiger)this.ProtectedM(); } } }
现在,如果您有D的实例,您将看到Tiger型的属性。如果将其强制转换为B,则会看到Animal- typed属性。无论哪种情况,您仍然可以通过受保护的成员获得虚拟行为。
简而言之,很抱歉,我们没有计划使用此功能。