2011-06-14 72 views
26

我有四個類。 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

回答

15

這不是真正構建的東西的好方法。執行以下操作之一:

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

1

編輯: 您不能更改類型上派生類型,但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

5

除了隱藏原物業:

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

0

這在理論上是不可能的。對於返回類型,覆蓋必須是協變的(也就是說,返回類型必須更具體或相同),並且參數是相反的(即參數類型必須較少特定或相同)。所以你的新類型必須同時有效地協調和逆變,就是說Request - 也就是說,唯一可能的類型就是Request

由於這個原因,C#中不允許更改覆蓋的屬性類型。

+0

不會派生的請求比請求更具體,並因此使返回類型的覆蓋協變?或者我讀了你的評論錯誤? – Trevor 2011-06-14 22:56:12

+0

@threed:一個屬性由一個吸氣劑和一個吸氣劑組成。問題在於setter :-) – Vlad 2011-06-14 22:57:13

6

在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

0
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; } 
} 
相關問題