2010-07-04 64 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