确定任何类型的列表,序列,数组或IEnumerable是否为空

cme*_*ren 5 f# ivalueconverter

我正在使用XAML为我的视图编写一个Xamarin.Forms应用程序,我正在尝试编写一个IValueConverter应该返回的工作,false如果输入对于那些语义有意义的类型是"空"(strings/lists/sequences/arrays/IEnumerables) ).我已经开始使用以下内容,它为空字符串返回false,但我无法弄清楚如何将它扩展到列表,序列,数组和IEnumerables:

type FalseIfEmptyConverter() =
  interface IValueConverter with 
    member __.Convert(value:obj, _, _, _) = 
      match value with
      | :? string as s -> (s <> "" && not (isNull s)) |> box
      // TODO: extend to enumerables
      | x -> invalidOp <| "unsupported type " + x.GetType().FullName

    member __.ConvertBack(_, _, _, _) =
      raise <| System.NotImplementedException()
Run Code Online (Sandbox Code Playgroud)

我尝试过的东西不起作用:

  • :? list<_> 与(盒装)列表(至少不是整数)不匹配并产生警告 This construct causes code to be less generic than indicated by its type annotations. The type variable implied by the use of a '#', '_' or other type annotation at or near [...] has been constrained to be type 'obj'
  • :? list<obj> 不会产生警告,但也不会与盒装的整体列表相匹配
  • 这是与同:? seq<_>:? seq<obj>
  • 这是与同:? System.Collections.Generic.IEnumerable<obj>IEnumerable<_>(如果我把它放在下面类似的seq比赛上面给出,它警告说,该规则将永远不会被匹配,这是有道理的,因为据我所知seq对应IEnumerable)

The*_*Fox 6

使用Foggy Finder的想法使用非泛型IEnumerable:

let isEmpty (x:obj) =
    match x with
    | null -> true
    | :? System.Collections.IEnumerable as xs -> xs |> Seq.cast |> Seq.isEmpty
    | _ -> invalidOp <| "unsupported type " + x.GetType().FullName

isEmpty "" // true
isEmpty [] // true
isEmpty (set []) // true
isEmpty [||] // true
isEmpty null // true

isEmpty "a" // false
isEmpty [|1|] // false

isEmpty 1 // exception
Run Code Online (Sandbox Code Playgroud)

您要测试的所有类型都是子类型Seq<'a>,与IEnumerable<'a>(包括string,a seq<char>)完全相同.但这也是一个非泛型类型的子类型IEnumerable(注意缺少类型参数).这与a类似IEnumerable<obj>,其中每个项目都已装箱.这就是为什么我们可以将所有这些转换为IEnumerable,然后使用Seq.cast它转换为IEnumerable<obj>我们可以使用Seq.empty,这只适用于泛型类型.

  • 在进行类型测试时,`string`是`seq <char>`,`char`是`obj`,但是`string`不是`seq <obj>`.对祖先类型的检查不会延伸到类型参数,即使它理论上可以.我不知道为什么F#表现得那样,或者它实际上是因为.NET,还是因为这里存在根本性的困难而只是缺乏这个功能. (2认同)