在没有指定实例类型的情况下,F#中是否存在对泛型类型进行类型测试的方法?

kol*_*osy 5 f#

我正在尝试模式匹配我关心SQL生成的几种类型.理想情况下我想这样做:

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()
Run Code Online (Sandbox Code Playgroud)

问题是,在这里,类型参数Option<_>获取约束以匹配,record最终只是obj.

我知道我可以做一些基于反射的检查(检查它是一个通用类型,并且它是基于名称的选项类型),但我宁愿避免这种情况,如果可能的话.

kvb*_*kvb 10

不,使用F#的内置结构没有好办法.但是,您可以为此类事物构建自己的可重用活动模式:

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"
Run Code Online (Sandbox Code Playgroud)

现在你的函数看起来像这样:

let rec getSafeValue (item:obj) = 
    match item with
    | :? string as str -> "'" + str + "'"
    | UC <@ Some @> [v] -> getSafeValue v
    | UC <@ None @> [] -> "null"
    | _ as v -> v.ToString()
Run Code Online (Sandbox Code Playgroud)