2010-07-04 65 views
1

我有一個令人沮喪的問題。我在ASP.NET MVC中構建了一個view engine,並正在實現接口IViewEngine。在其中一個方法中,我試圖動態找出視圖結果的類型。有時結果是一個模板(類型爲Template <'key>)。這些鍵用於在模板中定位佔位符,並且想法是使用歧視聯合,對於每個網站可能都是唯一的。它看起來是這樣的:現在實現接口的非泛型方法中的泛型模式匹配

type MasterKey = | HeadContent | HeaderContent | MainContent | FooterContent 
let MasterTemplate : Template<MasterKeys> = ... 

,問題是這樣的:因爲我實現一個接口,我有過的方法簽名沒有控制權。既然不能添加一個泛型類型參數中,「一個將被轉換爲一個OBJ和模板將不低於匹配:

match result with 
    | :? foo -> ... 
    | :? bar -> ... 
    | :? Template<'a> -> ... 

任何想法?

回答

1

您可以根據所使用的'key的類型使整個視圖引擎類具有通用性嗎?個別項目需要從視圖引擎類繼承,並指定過程中的鍵類型。

+0

事實證明,這是一個非常好的主意,它簡單直接! 我照你說的把這行添加到了Global.asax.cs中: ViewEngines.Engines.Add(new WingBeats.Mvc.WingBeatsTemplateEngine ()); 謝謝! (即使我掙扎了好幾個小時,我也覺得有點愚蠢,不能自己想出這個解決方案!) – 2010-07-05 21:22:22

6

不幸的是,沒有辦法很好地做到這一點。如果您可以控制Template<'T>類型,那麼最好的選擇是創建一個非通用接口(例如ITemplate)並在Template<'T>類型中實現該接口。然後,你可以檢查接口:

| :? ITemplate as t -> ... 

如果不是的話,那麼你唯一的選擇是使用一些魔法反射。您可以實現一個活動模式,該類型與某些Template<'T>值相匹配,並返回用作一般參數的類型列表(System.Type對象)。在你的僞代碼,你想獲得這個作爲泛型類型參數'a - 這是不可能得到,隨着編譯時類型參數,但你可以得到的運行時類型信息:

let (|GenericTemplate|_|) l = 
    let lty = typeof<list<int>>.GetGenericTypeDefinition() 
    let aty = l.GetType() 
    if aty.IsGenericType && aty.GetGenericTypeDefinition() = lty then 
    Some(aty.GetGenericArguments()) 
    else 
    None 

現在,您可以編寫以下模式匹配代碼:

match result with 
| GenericTemplate tys -> 
    // ... 

最後一個問題是 - 你怎麼可以使用這個運行時類型信息來運行一些通用的代碼。我能想到的最佳選擇是使用反射調用通用方法(或函數) - 然後您可以將運行時類型信息指定爲通用參數,因此代碼可以是通用的。最簡單的方法是調用類的靜態成員:

type TemplateHandler = 
    static member Handle<'T>(arg:Template<'T>) = 
    // This is a standard generic method that will be 
    // called from the pattern matching - you can write generic 
    // body of the case here... 
    "aaa" 

| :? GenericTemplate tys -> 
    // Invoke generic method dynamically using reflection 
    let gmet = typeof<TemplateHandler>.GetMethod("Handle").MakeGenericMethod(tys) 
    gmet.Invoke(null, [| result |]) :?> string // Cast the result to some type 

關鍵的想法是,你移動模式匹配的身體(不能有泛型類型參數)到一個方法(即可以有泛型類型參數)並使用反射動態運行該方法。

您可以更改代碼以使用let函數而不是static member - 使用反射來查找函數只會稍微困難一些。

+0

我喜歡這種方法,但是您可以通過基於泛型類型定義進行參數化來使活動模式更加一般化以進行匹配。請參閱http://stackoverflow.com/questions/2140079/how-to-cast-an-object-to-a-list-of-generic-type-in​​-f。 – kvb 2010-07-04 16:26:55

+0

@kvb:好戲!我嘗試編寫泛型活動模式,但似乎沒有辦法在用法中指定類型參數。使用引用似乎很好地工作(或者參數可能只是'(typedefof >)?) – 2010-07-04 17:12:13

+0

模式內只允許有限的語言元素子集(即使對於活動模式的參數),所以我不認爲你可以直接傳遞一個類型。據我所知,使用引語是語言允許的最乾淨的解決方案,但我很想證明是錯誤的! – kvb 2010-07-04 19:38:45