利用泛型的c#协变返回类型
下面的代码是实现协变返回类型的唯一方法吗利用泛型的c#协变返回类型,c#,generics,covariance,C#,Generics,Covariance,下面的代码是实现协变返回类型的唯一方法吗 public abstract class BaseApplication<T> { public T Employee{ get; set; } } public class Application : BaseApplication<ExistingEmployee> {} public class NewApplication : BaseApplication<NewEmployee> {} 我相
public abstract class BaseApplication<T> {
public T Employee{ get; set; }
}
public class Application : BaseApplication<ExistingEmployee> {}
public class NewApplication : BaseApplication<NewEmployee> {}
我相信这段代码工作得很好,但当我有几个属性需要相同的行为时,它会变得非常糟糕
是否有其他方法来实现此行为?泛型还是其他?我认为,您可以针对员工界面进行编码,以获得您想要的
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> {}
公共接口IEEmployee
{}
公共抽象类BaseApplication,其中T:IEmployee{
公共T雇员{get;set;}
}
现有的公共类Employee:IEEmployee{}
公共类NewEmployee:IEEmployee{}
公共类应用程序:BaseApplication{}
公共类NewApplication:BaseApplication{}
您发布的代码不会编译,但我知道您想做什么。简言之,答案是肯定的,这是唯一的办法。如果您希望属性在扩展类中返回不同类型的,并以不同类型的,则必须以现有的方式使用泛型
如果可以将employee对象(新对象或现有对象)的公共契约封装到接口中,那么根本不需要使用泛型。相反,您可以返回接口,让多态性接管
public interface IEmployee
{ }
public class Employee1 : IEmployee
{ }
public class Employee2 : IEmployee
{ }
public abstract class ApplicationBase
{
public abstract IEmployee Employee { get; set; }
}
public class App1 : ApplicationBase
{
public override IEmployee Employee
{
get { return new Employee1(); }
set;
}
}
public class App2 : ApplicationBase
{
public override IEmployee Employee
{
get { return new Employee2(); }
set;
}
}
您试图实现的目标可能存在多个问题 首先,正如有人已经注意到的,在您的示例中没有协方差。您可以在这里找到协方差和泛型的简短描述 其次,您似乎试图用泛型来解决多态性应该解决的问题。如果
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
希望这能有所帮助。一个想法没有泛型,但它还有其他缺点:
public abstract class BaseApplication {
public Employee Employee{ get; protected set; }
}
public class Application : BaseApplication
{
public new ExistingEmployee Employee{ get{return (ExistingEmployee)base.Employee;} set{base.Employee=value; }}
}
public class NewApplication : BaseApplication
{
public new NewEmployee Employee{ get{return (NewEmployee)base.Employee;} set{base.Employee=value; }}
}
特别是使用这段代码,您可以强制转换到基类,并分配不需要的类型的employee。因此,您需要在基类的setter中添加检查。或者移除setter,我通常更喜欢它。一种方法是保护setter。另一种方法是添加一个虚拟函数
EmployeeType()
,在派生类中重写该函数并返回派生类型。然后,如果EmployeeType().IsInstanceOf(value)
,则检查setter,否则抛出异常
IMO模拟协变收益类型是
新标记的少数几个良好应用之一。它返回与基类相同的内容,只是向函数契约添加了额外的保证。更新:这个答案是在2010年编写的。二十年来,人们一直在为C#提出回归型协方差,但现在看来,它最终会实现;我相当惊讶。有关公告,请参见的底部;我相信细节会随之而来。以下答案中推测正在实施的功能的可能性的部分,应仅考虑未来的历史利益
首先,您的问题的答案是否定的,C#不支持虚拟覆盖上任何形式的返回类型协方差
一些回答者和评论者说“这个问题没有协方差”。这是错误的;原来的海报完全正确地提出了他们的问题
回想一下。例如,从类型T
到类型IEnumerable
的映射是协变的,因为它保留了赋值兼容性关系。如果Tiger的赋值与Animal兼容,则地图下的转换也会保留:IEnumerable
的赋值与IEnumerable
兼容
这里的协变映射有点难看,但它仍然存在。问题的实质是:这是否合法
class B
{
public virtual Animal M() {...}
}
class D : B
{
public override Tiger M() {...}
}
老虎和动物是相容的。现在创建一个从类型T到方法“public T M()”的映射。这种映射是否保持了兼容性?也就是说,如果Tiger与Animal兼容用于赋值,那么出于虚拟覆盖的目的,public Tiger M()
是否与public Animal M()
兼容
C#中的答案是“不”。C#不支持这种协方差
现在,我们已经确定问题是用正确的类型代数术语提出的,对实际问题还有一些想法。显而易见的第一个问题是,该财产甚至还没有被声明为虚拟财产,因此虚拟兼容性的问题是没有意义的。第二个明显的问题是,“get;set;”属性不可能是协变的,即使C#确实支持返回类型协变,因为带有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();
嘿,我们刚刚把一只长颈鹿递给了一只正在等老虎的setter。如果我们支持此功能,我们将不得不将其限制为返回类型(就像我们在泛型接口上对赋值兼容性协方差所做的那样)
第三个问题是CLR不支持这种差异;如果我们想用语言支持它(我相信托管C++),那么我们就必须在CLR。< /P>中围绕签名匹配限制来做一些合理的英勇措施。
通过仔细定义“新”方法,您可以自己进行这些英勇的度量,这些方法具有适当的返回类型,这些返回类型会影响它们的基类类型:
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,那么你会看到动物类型的属性
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(); } }
}
using System;
namespace return_type_covariance
{
public interface A1{}
public class A2 : A1{}
public class A3 : A1{}
public interface B1
{
A1 theA();
}
public class B2 : B1
{
public A1 theA()
{
return new A2();
}
}
public static class B2_ReturnTypeCovariance
{
public static A2 theA_safe(this B2 b)
{
return b.theA() as A2;
}
}
public class B3 : B1
{
public A1 theA()
{
return new A3();
}
}
public static class B3_ReturnTypeCovariance
{
public static A3 theA_safe(this B3 b)
{
return b.theA() as A3;
}
}
public class C2
{
public void doSomething(A2 a){}
}
class MainClass
{
public static void Main (string[] args)
{
var c2 = new C2();
var b2 = new B2();
var a2=b2.theA_safe();
c2.doSomething(a2);
}
}
}
public class Base
{
public virtual T Foo<T>() where T : Base
{
//... // do stuff
return (T)this;
}
}
public class A : Base
{
public A Bar() { "Bar".Dump(); return this; }
public A Baz() { "Baz".Dump(); return this; }
// optionally override the base...
public override T Foo<T>() { "Foo".Dump(); return base.Foo<T>(); }
}
var x = new A()
.Bar()
.Foo<A>() // cast back to A
.Baz();