据我所知,无法在C#2.0中执行以下操作
public class Father
{
public virtual Father SomePropertyName
{
get
{
return this;
}
}
}
public class Child : Father
{
public override Child SomePropertyName
{
get
{
return this;
}
}
}
我通过在派生类中将属性创建为“new”来解决问题,但当然这不是多态的。
public new Child SomePropertyName
2.0中有什么解决方案吗? 3.5中解决此问题的任何功能如何?
答案 0 :(得分:41)
您可以重新声明(新),但不能同时重新声明和覆盖(使用相同的名称)。 一种选择是使用受保护的方法来隐藏细节 - 这允许同时进行多态和隐藏:
public class Father
{
public Father SomePropertyName
{
get {
return SomePropertyImpl();
}
}
protected virtual Father SomePropertyImpl()
{
// base-class version
}
}
public class Child : Father
{
public new Child SomePropertyName
{
get
{ // since we know our local SomePropertyImpl actually returns a Child
return (Child)SomePropertyImpl();
}
}
protected override Father SomePropertyImpl()
{
// do something different, might return a Child
// but typed as Father for the return
}
}
答案 1 :(得分:25)
由于类型安全问题,这在任何.NET语言中都是不可能的。在类型安全的语言中,您必须为返回值和参数的逆变提供协方差。拿这个代码:
class B {
S Get();
Set(S);
}
class D : B {
T Get();
Set(T);
}
对于Get
方法,协方差意味着T
必须是S
或从S
派生的类型。否则,如果您对类型为D
的对象的引用存储在变量类型B
中,则当您调用B.Get()
时,您将无法将对象表示为S
反击 - 打破类型系统。
对于Set
方法,逆变意味着T
必须是S
或S
派生的类型。否则,如果您引用了存储在变量类型D
中的B
类型的对象,则当您调用B.Set(X)
时,其中X
的类型为S
但不是类型T
,D::Set(T)
会得到一个它没有预料到的类型的对象。
在C#中,有意识地决定在重载属性时禁止更改类型,即使它们只有一个getter / setter对,因为否则会有非常不一致的行为(“你的意思是,我可以使用getter更改一个类型,但不能同时使用getter和setter更改类型?为什么不呢?!?“ - Anonymous Alternate Universe Newbie)。
答案 2 :(得分:11)
不,但您可以在2及以上版本中使用泛型:
public class MyClass<T> where T: Person
{
public virtual T SomePropertyName
{
get
{
return ...;
}
}
}
然后父亲和孩子是同一类的通用版本
答案 3 :(得分:7)
来自Wikipedia:
在C#编程语言中,支持return-type 协方差和参数 增加了代表们的逆转 在该语言的2.0版本中。 既不是协方差也不是逆变 支持方法覆盖。
虽然没有明确说明属性的协方差。
答案 4 :(得分:2)
您可以为父子创建一个公共接口,并返回该接口的类型。
答案 5 :(得分:1)
没有。 C#不支持这个想法(它被称为“返回类型协方差”)。 但是你可以这样做:
public class FatherProp
{
}
public class ChildProp: FatherProp
{
}
public class Father
{
public virtual FatherProp SomePropertyName
{
get
{
return new FatherProp();
}
}
}
public class Child : Father
{
public override FatherProp SomePropertyName
{
get
{
// override to return a derived type instead
return new ChildProp();
}
}
}
即。使用基类定义的契约,但返回派生类型。我已经做了一个更详细的例子来使这一点更清楚 - 再次返回“this”不会改变任何东西。
对于它的实际类型(即“if someObject is ChildProp”)测试返回的对象是可能的(但是很麻烦),但最好在它上面调用一个对其类型做正确事情的虚拟方法。 / p>
基类虚方法(在本例中为虚拟属性)不仅具有实现,还定义了一个契约:如果子类符合此契约,则子类可以提供SomePropertyName的不同实现(即SomePropertyName返回一个对象)类型“FatherProp”)。返回源自“FatherProp”的“ChildProp”类型的对象符合此合同。但是你不能改变“孩子”中的合同 - 这个合同适用于所有来自“父亲”的类。
如果您退后一步并查看更广泛的设计,C#工具箱中还有其他语言结构,您可能还需要考虑这些结构 - 泛型或接口。
答案 6 :(得分:1)
没有。 C#不支持这个想法 (它被称为“返回类型 协方差“)。
来自维基百科:
在C#编程语言中, 支持返回类型 协方差和参数 增加了代表们的逆转 在该语言的2.0版本中。 既不是协方差也不是逆变 支持方法覆盖。
你可以重新声明(新),但是你 无法重新声明和覆盖 同一时间(同名)。一 选项是使用受保护的方法 隐藏细节 - 这允许两者 多态性和隐藏在同一个 时间:
最好的解决方案是使用泛型:
public class MyClass<T> where T: Person
{
public virtual T SomePropertyNameA
{
get { return ...; }
}
}//Then the Father and Child are generic versions of the same class
答案 7 :(得分:1)
这是我能来的最近的(到目前为止):
public sealed class JustFather : Father<JustFather> {}
public class Father<T> where T : Father<T>
{ public virtual T SomePropertyName
{ get { return (T) this; }
}
}
public class Child : Father<Child>
{ public override Child SomePropertyName
{ get { return this; }
}
}
如果没有JustFather
类,除非是其他派生类型,否则无法实例化Father<T>
。
答案 8 :(得分:0)
从 C# 9 开始,return type covariance is supported。这是从该链接复制的基本示例:
class Compilation ...
{
public virtual Compilation WithOptions(Options options)...
}
class CSharpCompilation : Compilation
{
public override CSharpCompilation WithOptions(Options options)...
}