Is there a way in F# to type-test against a generic type without specifying the instance type?

前端 未结 4 1871
春和景丽
春和景丽 2021-01-01 02:45

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: Prope         


        
4条回答
  •  遥遥无期
    2021-01-01 03:27

    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, 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()
    

提交回复
热议问题