为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
翻过高山走不出你 2019-12-02 13:12:14
下面的代码是实现协变返回类型的唯一方法吗?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我相信这段代码可以正常工作,但是当我有多个需要相同行为的属性时,它确实很讨厌。还有其他方法可以实现此行为吗?泛型还是其他?
查看完整描述

3 回答

?
收到一只叮咚

TA贡献1821条经验 获得超4个赞

首先,您的问题的答案是否定的,C#不支持任何形式的虚拟覆盖类型的返回类型协方差。


许多回答者和评论者说“这个问题没有协方差”。这是不正确的。原始海报完全像他们一样提出问题。


回想一下,协变映射是保留了某些其他关系的存在和方向的映射。例如,从类型T到类型的映射IEnumerable<T>是协变的,因为它保留了分配兼容性关系。如果Tiger与Animal分配兼容,则地图下的变换也将保留: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()吗?


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属性。无论哪种情况,您仍然可以通过受保护的成员获得虚拟行为。


简而言之,很抱歉,我们没有计划使用此功能。


查看完整回答
反对 回复 2019-12-02
?
潇湘沐

TA贡献1816条经验 获得超6个赞

您尝试实现的目标可能存在多个问题。


首先,正如已经有人注意到的那样,您的示例中没有协方差。您可以在这里找到协方差和泛型的简短描述,C#2.0的新功能-Variance,泛型的协方差。


其次,似乎您尝试使用泛型来解决应使用多态性解决的问题。如果同时ExistingEmployee和NewEmployee继承从基类Employee,您的问题将得到解决:


public class Application {

    public ExistingEmployee Employee { get; }

}


public class NewApplication {

    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 

                             // declared as Employee because of polymorphism

多态和泛型之间的不同之处在于,如果将变量返回到特定类型,则在后面的情况下需要进行强制转换:


ExistingEmployee emp = (ExistingEmployee)app.Employee;  // would have not been needed 

                                                        // if working with generics

希望这可以帮助。


查看完整回答
反对 回复 2019-12-02
?
猛跑小猪

TA贡献1858条经验 获得超8个赞

您可以针对员工界面编写代码,以得到您想要的信息。


public interface IEmployee

{}


public abstract class BaseApplication<T> where T:IEmployee{ 

    public T IEmployee{ get; set; } 


public class ExistingEmployee : IEmployee {}

public class NewEmployee : IEmployee {}


public class Application : BaseApplication<ExistingEmployee> {} 


public class NewApplication : BaseApplication<NewEmployee> {} 


查看完整回答
反对 回复 2019-12-02
  • 3 回答
  • 0 关注
  • 584 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信