Joh*_*son 2 f# pattern-matching
我有一个令人沮丧的问题.我正在ASP.NET MVC中构建一个视图引擎,并正在实现接口IViewEngine.在其中一种方法中,我试图动态地找出视图结果的类型.有时结果是模板(类型为Template <'key>).密钥用于在模板中定位占位符,其目的是使用区分联合,可能对每个网站都是唯一的.它可能看起来像这样:
type MasterKey = | HeadContent | HeaderContent | MainContent | FooterContent
let MasterTemplate : Template<MasterKeys> = ...
Run Code Online (Sandbox Code Playgroud)
现在,问题是这样的:因为我正在实现一个接口,所以我无法控制方法签名.由于我无法添加泛型类型参数,因此'a'将转换为obj,而下面的模板将不匹配:
match result with
| :? foo -> ...
| :? bar -> ...
| :? Template<'a> -> ...
Run Code Online (Sandbox Code Playgroud)
有任何想法吗?
不幸的是,没有办法很好地做到这一点.如果您可以控制Template<'T>类型,那么最好的选择是创建一个非泛型接口(例如ITemplate)并在Template<'T>类型中实现它.然后你可以检查界面:
| :? ITemplate as t -> ...
Run Code Online (Sandbox Code Playgroud)
如果情况并非如此,那么你唯一的选择是使用一些反射魔法.您可以实现一个活动模式,该模式在类型为某个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
Run Code Online (Sandbox Code Playgroud)
现在您可以编写以下模式匹配代码:
match result with
| GenericTemplate tys ->
// ...
Run Code Online (Sandbox Code Playgroud)
最后一个问题是 - 如何使用此运行时类型信息来运行一些通用代码.我能想到的最佳选择是使用反射调用泛型方法(或函数) - 然后您可以将运行时类型信息指定为通用参数,因此代码可以是通用的.最简单的选择是调用类型的静态成员:
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
Run Code Online (Sandbox Code Playgroud)
关键思想是将模式匹配的主体(不能具有泛型类型参数)移动到方法(可以具有泛型类型参数)中,并使用反射动态运行方法.
您可以将代码更改为使用let函数而不是使用函数static member- 使用反射查找函数稍微困难一些.