F#中有没有一种方法可以在不指定实例类型的情况下针对泛型类型进行类型测试? [英] Is there a way in F# to type-test against a generic type without specifying the instance type?
问题描述
我正在尝试对我关心的用于SQL生成的几种类型进行模式匹配.理想情况下,我想这样做:
I'm trying to pattern match against a few types that I care about for SQL generation. Ideally I'd like to do this:
let rec getSafeValue record (prop: PropertyInfo) =
match prop.GetValue(record, null) with
| :? string as str -> "'" + str + "'"
| :? Option<_> as opt ->
match opt with
| Some v -> getSafeValue v prop
| None -> "null"
| _ as v -> v.ToString()
问题在于,这里Option<_>
的类型参数受到约束,以匹配record
的类型参数,而最终成为obj
.
The problem is that here, the type parameter to Option<_>
gets constraint to match that of record
, which ends up being just obj
.
我知道我可以做一些基于反射的痛苦检查(检查它是泛型类型,还是基于名称的选项类型),但我宁愿避免这种情况(如果可能)
I know I can do some pain-in-the-behind reflection-based check (check that it's a generic type and that it's an option type based on the name), but I'd rather avoid that if at all possible.
推荐答案
否,没有很好的方法使用F#的内置结构来做到这一点.但是,您可以为这种事情建立自己的可重用活动模式:
No, there's no good way to do this using F#'s built-in constructs. However, you could build your own reusable active pattern for this sort of thing:
open Microsoft.FSharp.Reflection
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.DerivedPatterns
open Microsoft.FSharp.Quotations.Patterns
let (|UC|_|) e o =
match e with
| Lambdas(_,NewUnionCase(uc,_)) | NewUnionCase(uc,[]) ->
if (box o = null) then
// Need special case logic in case null is a valid value (e.g. Option.None)
let attrs = uc.DeclaringType.GetCustomAttributes(typeof<CompilationRepresentationAttribute>, false)
if attrs.Length = 1
&& (attrs.[0] :?> CompilationRepresentationAttribute).Flags &&& CompilationRepresentationFlags.UseNullAsTrueValue <> enum 0
&& uc.GetFields().Length = 0
then Some []
else None
else
let t = o.GetType()
if FSharpType.IsUnion t then
let uc2, fields = FSharpValue.GetUnionFields(o,t)
let getGenType (t:System.Type) = if t.IsGenericType then t.GetGenericTypeDefinition() else t
if uc2.Tag = uc.Tag && getGenType (uc2.DeclaringType) = getGenType (uc.DeclaringType) then
Some(fields |> List.ofArray)
else None
else None
| _ -> failwith "The UC pattern can only be used against simple union cases"
现在您的函数可能看起来像这样:
Now your function might look something like this:
let rec getSafeValue (item:obj) =
match item with
| :? string as str -> "'" + str + "'"
| UC <@ Some @> [v] -> getSafeValue v
| UC <@ None @> [] -> "null"
| _ as v -> v.ToString()
这篇关于F#中有没有一种方法可以在不指定实例类型的情况下针对泛型类型进行类型测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!