在c中覆盖具有派生返回类型的抽象属性

问题描述:

我有四个类。 Request,DerivedRequest,Handler,DerivedHandler。处理程序类具有以下声明的属性:在c中覆盖具有派生返回类型的抽象属性

public abstract Request request { get; set; } 

的DerivedHandler需要,以便它返回DerivedRequest而不是覆盖此属性:

public override DerivedRequest request { get; set; } 

有没有人有关于如何使这项工作任何想法?

+0

这不是严格把好OOP,因为它违反了接口。某些类型的setter操作(带有非常值的'')会意外抛出。 – recursive 2011-06-14 22:46:11

+0

在这种情况下,我想,我不需要一个setter。我可以创建一个私有属性并将其设置在构造函数中。这会照顾setter操作例外,是吗? – Trevor 2011-06-14 22:59:04

+0

在这种情况下,您不需要重写该属性。只要构造函数只接受DerivedRequest。 – recursive 2011-06-15 02:28:22

这不是真正构建的东西的好方法。执行以下操作之一:

1)只是不要更改返回类型,并在子类中正常覆盖它。在DerivedHandler中,您可以使用Request的基类签名返回DerivedRequest的实例。任何使用此代码的客户端代码都可以选择将其转换为DerivedRequest(如果他们愿意的话)。

2)如果它们不应该是多态的,请改用泛型。

public abstract class HandlerBase<T> where T: Request 
{ 
    public abstract T Request {get;set;} 
} 

public class Handler: HandlerBase<Request>() 

public class DerivedHandler: HandlerBase<DerivedRequest>() 
+0

如果存在多种情况,例如我想以使用派生类的方式重写的多个属性,那么我需要执行类似于以下操作的类:class HandlerBase 其中A:classA,B:classB,...' ? – Javidan 2017-02-06 12:15:49

编辑: 您不能更改类型上派生类型,但new可能帮助:

在派生类...

public new DerivedRequest request 
{ 
    get{return (DerivedRequest) base.request;} 
    set{base.request = value;} 
} 
public override Request request 
{ 
    get{return base.request;} 
    set{base.request = (DerivedRequest) value;} // Throws InvalidCastException if misused. 
} 
+0

这通常不被称为“覆盖” – Vlad 2011-06-14 22:45:55

除了隐藏原物业:

public new DerivedRequest Request { get;set;} 

但是,我强烈建议不要这样做。隐藏应该被覆盖的东西会引起麻烦,特别是如果该属性不是简单的自动生成的属性。另外,如果使用它作为接口或基类,原始实现(在这种情况下,继承树中的一个类更高)。如果你正在实现一个抽象类或接口,你甚至不能隐藏原始签名,因为你需要实现它。

通常情况下,如果您考虑使用new关键字,则说明您的方法错误。在有些情况下,它是必要和必需的,然而,在大多数情况下,事实并非如此。

相反,使他人财产:

public DerivedRequest DerivedRequest {/* make adequate conversions here*/ } 

这样的话,你是关于面向对象的明确侧,你会得到一个明确的方式您的信息。

+0

那么,你将如何处理二传手? – Vlad 2011-06-14 22:55:11

+1

任何'DerivedRequest'都是一个'Request',可以直接分配。 – Femaref 2011-06-14 23:12:03

这在理论上是不可能的。对于返回类型,覆盖必须是协变的(也就是说,返回类型必须更具体或相同),并且参数是相反的(即参数类型必须较少特定或相同)。所以你的新类型必须同时有效地协调和逆变,就是说Request - 也就是说,唯一可能的类型就是Request

由于这个原因,C#中不允许更改覆盖的属性类型。

+0

不会派生的请求比请求更具体,并因此使返回类型的覆盖协变?或者我读了你的评论错误? – Trevor 2011-06-14 22:56:12

+0

@threed:一个属性由一个吸气剂和一个吸气剂组成。问题在于setter :-) – Vlad 2011-06-14 22:57:13

在C#语言中,您是不允许更改继承方法的签名,除非您将其替换为另一个同名方法。这种技术被称为“会员隐藏”或“阴影”。

如果您使用的是.NET 2.0或更高版本,则可以通过将Request属性的返回类型转换为Handler类的泛型类型参数来解决此问题。然后DerivedHandler类将指定DerivedRequest类作为该类型参数的参数。

下面是一个例子:

// Handler.cs 
public class Handler<TRequest> where TRequest : Request 
{ 
    public TRequest Request { get; set; } 
} 

// DerivedHandler.cs 
public class DerivedHandler : Handler<DerivedRequest> 
{ 
} 
+2

你当然可以这样做;只要Liskov替代原则得以维持,“OOP”中的任何内容都不得阻止你这样做。例如,在面向对象语言“C++”中,重写一个返回一个返回Tiger的Animal的方法是合法的。这个特性被称为“返回类型协方差”,它在OOP语言中相当普遍。但它不是C#的一个特性。 – 2011-06-15 03:51:58

+0

@Eric Lippert你是对的。我熟悉_return类型协变_和_parameter类型contravariance_。自1.0以来,C#为代表类型提供了支持。后来C#4.0增加了对通用接口和委托类型的支持。谢谢你指出我的错误,我纠正了答案。 – 2011-06-15 09:10:32

public class Request{} 

public class DerivedRequest : Request{} 

public class Handler<T> 
    where T : Request 
{ 
    public abstract T Request { get; set; } 
} 

public class DerivedHandler : Handler<DerivedRequest> 
{ 
    public override DerivedRequest Request { get; set; } 
}