2012-02-23 31 views
3

我試圖編譯下面的代碼:將混凝土類型澆鑄成普通類型?

public class BaseRequest<TResponse> where TResponse : BaseResponse {} 
public class BaseResponse {} 

public class FooRequest : BaseRequest<FooResponse> {} 
public class FooResponse : BaseResponse {} 

... 

public TResponse MakeRequest<TResponse>(BaseRequest<TResponse> request) 
    where TResponse : BaseResponse 
{ 
} 

我希望我可以打電話MakeRequest(new FooRequest())並獲得返回值FooResponse。被叫方不必知道關於FooRequest並可能將其傳遞給另一個處理程序。簽名工作正常,但我無法實現MakeRequest方法。如果我實現它想:

public TResponse MakeRequest<TResponse>(BaseRequest<TResponse> request) 
    where TResponse : BaseResponse 
{ 
    FooRequest fooRequest = request as FooRequest; 
    if (fooRequest != null) // if I can handle the request, handle it 
    { 
     return new FooResponse(...); // *** 
    } 

    BarRequest barRequest = request as BarRequest; 
    if (barRequest != null) 
    { 
     return new BarResponse(...); 
    } 

    else      // otherwise, pass it on to the next node 
    { 
     // maybe it will handle a BazRequest, who knows 
     return nextNode.MakeRequest(request); 
    } 
} 

***行不能編譯,因爲編譯器不知道FooResponseTResponse。我知道這是因爲它在FooRequest中指定。有沒有辦法解決這個問題,而不涉及討厭的反思(在這種情況下,我寧願返回BaseResponse)?

謝謝。

更新:我正在使用泛型來強制執行返回類型,因此調用站點完全知道該期待什麼。僅僅在這裏返回BaseResponse會容易得多,但它將負責確定具體的返回類型給調用者而不是請求處理程序(當然瞭解所有關於輸入的知識)。

+7

首先,不應該將該方法稱爲MakeResponse,因爲它返回一個TResponse?但更一般地說:如果你必須檢查一個事物的類型,並且對某些特定類型採取了一些特定的操作,那麼**你並不是首先編寫泛型代碼**,那麼爲什麼你要使用泛型*?如果你有特殊的邏輯知道如何將一個FooRequest變成一個FooResponse,那麼創建一個接受FooRequest並返回一個FooResponse的方法;沒有泛型要求。 – 2012-02-23 19:46:04

+0

@Eric - 我正在制定一個責任鏈模式,在這個模式中路由節點只是傳遞消息,並且可能中間的某個節點識別請求並返回正確的響應。不過,我應該在代碼示例中說清楚。 – 2012-02-23 19:51:23

+0

@forcey CoR不暗示任何關於泛型的知識,而實例類型的匹配不是泛型的目的。只需傳遞BaseRequests並使用'if(request is FooRequest){...}' – 2012-02-23 19:55:35

回答

7

正如我在評論中所說,我懷疑你做錯了。這看起來像是對泛型的濫用。

這就是說,你告訴編譯器「我知道比你更多的類型信息」的方式是通過強制轉換。

var response = new FooResponse(...); 
return (TResponse)(object)response; 

演員爲對象,然後到τ響應告訴編譯器:「我知道,有從反應到τ響應的身份,拆箱或引用轉換」。如果你錯了,你會在運行時得到一個異常。

+0

謝謝 - 看起來我可以將FooResponse投射到BaseResponse然後投射到TResponse。我仍然認爲我不會濫用它:) – 2012-02-23 19:57:46

+1

@forcey,泛型應該是通用的。如果在處理通用參數時進行了投射操作,則很可能會濫用該系統。 – 2012-02-23 20:34:01

+0

@RomanR。 - 足夠公平的,我意識到我只是使用泛型提供的類型扣除系統,而不是以它應該的方式使用泛型。 – 2012-02-23 20:49:22

2

在我看來,你應該從BaseRequest非通用版本派生BaseRequest<T>類,然後寫你的函數爲:

public BaseResponse MakeRequest(BaseRequest request) 

在我看來這是一個正確的方式做到這一點,因爲你不甚至指的是函數內部的類型。

在我看來,仿製藥只在這裏作爲句法糖。什麼你從編寫函數,你做的方式獲得是要能寫:

FooResponse r = MakeRequest(new FooRequest(...)) 

的這個代替:

FooResponse r = (FooResponse)MakeRequest(new FooRequest(...)) 

因此上漲空間並不大。事實上,你被自己的代碼弄糊塗了,以至於你看不到缺少的東西,這意味着代碼可能比非泛型方式更不明顯。

哦,你的方法的另一個缺點是,你將失去能力做:

var requests = new List<BaseRequest> { new FooRequest(), new BarRequest() }; 
var responses = new List<BaseResponse>(); 
foreach(var request in requests) 
{ 
    responses.Add(MakeRequest(request)); 
} 

或者你可以做的是有:

public BaseResponse MakeRequest(BaseRequest request) { /* thing that does the work */ } 
public TResponse MakeRequest<TResponse>(BaseRequest<TResponse> request) 
{ 
    // Just for the nice syntax 
    return (TResponse)MakeRequest(request); 
} 

但是這看起來確實令人費解。無論如何,我會讓你反思一下

+0

非常感謝,這確實非常有幫助。 – 2012-02-23 21:21:26

相關問題