使用泛型的C#协变量返回类型

下面的代码是实现协变返回类型的唯一方法吗?


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

我相信这段代码可以正常工作,但是当我有多个需要相同行为的属性时,它确实很讨厌。


还有其他方法可以实现此行为吗?泛型还是其他?


翻过高山走不出你
浏览 586回答 3
3回答

收到一只叮咚

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

潇湘沐

您尝试实现的目标可能存在多个问题。首先,正如已经有人注意到的那样,您的示例中没有协方差。您可以在这里找到协方差和泛型的简短描述,C#2.0的新功能-Variance,泛型的协方差。其次,似乎您尝试使用泛型来解决应使用多态性解决的问题。如果同时ExistingEmployee和NewEmployee继承从基类Employee,您的问题将得到解决:public class Application {&nbsp; &nbsp; public ExistingEmployee Employee { get; }}public class NewApplication {&nbsp; &nbsp; public NewEmployee Employee { get; }}...Application app = new Application;var emp = app.Employee; // this will be of type ExistingEmployee!请注意,以下内容也适用:Employee emp = app.Employee; // this will be of type ExistingEmployee even if&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// declared as Employee because of polymorphism多态和泛型之间的不同之处在于,如果将变量返回到特定类型,则在后面的情况下需要进行强制转换:ExistingEmployee emp = (ExistingEmployee)app.Employee;&nbsp; // would have not been needed&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // if working with generics希望这可以帮助。

猛跑小猪

您可以针对员工界面编写代码,以得到您想要的信息。public interface IEmployee{}public abstract class BaseApplication<T> where T:IEmployee{&nbsp;&nbsp; &nbsp; public T IEmployee{ get; set; }&nbsp;}&nbsp;public class ExistingEmployee : IEmployee {}public class NewEmployee : IEmployee {}public class Application : BaseApplication<ExistingEmployee> {}&nbsp;public class NewApplication : BaseApplication<NewEmployee> {}&nbsp;
打开App,查看更多内容
随时随地看视频慕课网APP