我有一个令人沮丧的问题。我正在ASP.NET MVC中构建view engine并正在实现接口IViewEngine。在其中一种方法中,我试图动态地找出视图结果的类型。有时结果是模板(模板<'键>类型)。密钥用于在模板中定位占位符,其目的是使用区分联合,可能对每个网站都是唯一的。它看起来像这样:
type MasterKey = | HeadContent | HeaderContent | MainContent | FooterContent
let MasterTemplate : Template<MasterKeys> = ...
现在,问题是这样的:因为我正在实现一个接口,所以我无法控制方法签名。由于我无法添加泛型类型参数,因此'a'将转换为obj,模板将不会匹配:
match result with
| :? foo -> ...
| :? bar -> ...
| :? Template<'a> -> ...
有什么想法吗?
答案 0 :(得分: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
- 使用反射查找函数稍微困难一些。
答案 1 :(得分:1)
根据它使用的'key
的类型,你能否使整个视图引擎类具有通用性? Indivudual项目需要从您的视图引擎类继承并指定该过程中的键类型。