2012-01-12 59 views
1

我有以下類別:上傳幫助程序的方法?

class Alpha 
{ 
} 
class Beta : Alpha 
{ 
} 
class Gamma : Beta 
{ 
} 
class Epsilon : Beta 
{ 
} 

而且我有幾個方法,我把它們作爲參數:

void Receiver(Gamma gamma); 
void Receiver(Epsilon epsilon); 
void Receiver(Beta beta); 
void Receiver(Alpha alpha); 

我需要的東西有點不同尋常的位置。
我希望能夠將Alpha對象傳遞給所有方法,但我不希望像void Receiver(Beta beta);這樣的方法能夠接收從Beta繼承的類型的對象(也就是說,我想,最壞的情況下會提出Exception if一個GammaEpsilon對象傳遞。

我怎樣才能最好以這樣一種方式,是通用實現這一點?

我想在第一部分,我應該有一個方法,它允許上溯造型,這種as

class Alpha 
{ 
    public T Upcast<T>() where T : Alpha 
    { 
     // somehow return a T 
    } 
} 

這裏的問題是,我希望這能夠在各個層面上工作,例如,我希望能夠「向上」Beta

第二部分應該更容易一些,因爲我只是拋出高於我需要的類型而被傳遞的對象。

所以這樣的事情就夠了:

void Receiver(Epsilon epsilon) 
{ 
    // if epsilon inherits from Epsilon, throw. 
} 

你能幫我與我的問題的第一部分?謝謝!

+11

如果你有一個接受'Base'作爲參數的方法,並且不能與Derived一起工作,那麼你的類設計是錯誤的。這就是你需要解決的問題。 – Jon 2012-01-12 01:01:09

+0

這是一個特殊的情況,我使用反射來解析屬性到查詢字符串參數中,我不想接受派生類,因爲那些屬性的屬性會導致相關API調用中的非法查詢字符串參數。 – bevacqua 2012-01-12 01:09:26

+0

雖然也許所有的東西都應該被'密封',但我仍然明白你的觀點,但是對我來說重要的是上傳方法。 – bevacqua 2012-01-12 01:10:17

回答

1

你似乎有代碼,可以對付類型掙扎,但它不能被信任亞型有妥善處理。這通常違反了許多OOP原則,其中之一是LSP,或者是Liskov Substitution Principle

你可能會在一個地方,你可以考慮visitor pattern作爲克服這個問題的一種手段。它與你已有的非常接近,只需稍作修改即可!模式從你的代碼中出現,而不是強加在它上面,這很好。

您的void Receiver方法實質上已經是訪客實現,所以讓我們重新命名它們並創建一個接口。

interface IVisitor 
{ 
    void Visit(Gamma gamma); 
    void Visit(Epsilon epsilon); 
    void Visit(Beta beta); 
    void Visit(Alpha alpha); 
} 

您正在處理每種元素類型的處理的類只需實現該接口。

class Visitor : IVisitor 
{ 
    public void Visit(Gamma gamma) { Console.WriteLine("Visiting Gamma object"); } 
    public void Visit(Epsilon epsilon) { Console.WriteLine("Visiting Epsilon object"); } 
    public void Visit(Beta beta) { Console.WriteLine("Visiting Beta object"); } 
    public void Visit(Alpha alpha) { Console.WriteLine("Visiting Alpha object"); } 
} 

然後讓每個元素類型也實現一個簡單的和通用的接口

interface IElement { void Accept(IVisitor visitor); } 

class Alpha : IElement 
{ 
    public virtual void Accept(IVisitor visitor) 
    { 
     visitor.Visit(this); 
    } 
} 

class Beta : Alpha 
{ 
    public override void Accept(IVisitor visitor) 
    { 
     visitor.Visit(this); 
    } 
} 

// etc. 

(嚴格地說,接口不必要,這是一個好的形式。)

而且你去了,一個雙重調度的方法,其中一個元素調用訪問方法特定於它自己的類型,即使你可能通過基類型引用它。

Alpha alpha = new Beta(); 
IVisitor visitor = new Visitor(); 
alpha.Accept(visitor); 

如果你已經正確地實現它,你會看到屏幕上的「訪問測試版的對象」(或者,在你的條件,你應該看到所期望的「接收器」的行爲執行。)


您可能考慮的另一個選擇是簡單地將Receiver作爲基類的虛擬方法,並讓派生類重寫它,並在類中包含行爲(如果這對您的層次結構有意義)。如果行爲需要是外部的,你也可以考慮知道如何爲給定類創建特定處理器的工廠方法。如果一個子類型傳遞給一個期望基類的方法,那麼有很多選項可以不涉及到你的程序。


我需要的東西有點不同尋常的位置。我希望能夠通過 Alpha對象的所有方法,但我不希望像void 接收器(測試版)的方法;能夠接受的是 從Beta繼承類型的對象(即,我想,在最壞的情況,提高如果 伽瑪或ε對象傳遞一個例外。

這是很奇怪的。我我不確定你是否真的想要這樣,你似乎將繼承層次顛倒過來,超級類型可以替代派生類型,而不是反過來,你應該重新思考你正在嘗試做什麼。

0

你可以做一個GetType並檢查它的測試版。

+0

我想上傳它,不確定它是派生類型。 – bevacqua 2012-01-12 01:28:45

+0

從epsilon製作新的測試版? – 2012-01-12 01:30:16

+0

從Beta版製作一個新的'Epsilon' – bevacqua 2012-01-12 01:33:59

0

我希望能夠爲Alpha對象傳遞給所有方法

你不能這樣做可靠。 Alpha不是Gamma,因此您無法可靠地將其用作Receiver(Gamma)的參數。否則,你可以這樣做:

Alpha a = new Gamma(); // legal 
Receiver((Epsilon)a); // not legal 

如果你定義爲你在你的問題做了接收器功能,編譯器會選擇基於聲明的類型正確的方法(或鑄造類型,如果你投的話):

Epsilon e = new Epsilon(); 
Alpha a = new Gamma(); 

Receiver(e);   // calls Receiver(Epsilon) 
Receiver(a);   // calls Receiver(Alpha) 
Receiver((Gamma)a); // calls Receiver(Gamma) 
Receiver((Beta)e); // calls Receiver(Beta) 

因此,如果您正確調用它,編譯器將選擇正確的方法。

,但我不希望像void Receiver(Beta beta);方法能夠接受的,從測試版

繼承那麼你有沒有使用繼承正確類型的對象。 GammaBeta。爲什麼要防止繼承類型使用該方法?如果您希望Gamma和Beta共享某些屬性/方法而無需繼承,請選擇其他模式,如封裝組合並使用接口。

這就是說,你上溯造型功能應該是簡單的:

public T Upcast<T>() where T : Alpha 
{ 
    return this as T; // will return null is this is not a T 
} 

... 

new Beta().Upcast<Gamma>(); // will return null 
new Epsilon().Upcate<Epsilon>(); // will return the Epsilon