有没有一种安全的方法将Collection转换为F#中的序列?

Car*_*nez 6 .net f# functional-programming

大家下午好!

所以我一直在讨论将.NET Collection转换为功能数据结构的方法.我能得到的最好的就是先将它投射到seq,然后投入到我想要的任何东西之后.

问题是,这似乎打破了类型推断,这显然是不安全的.

例:

let a = new System.DirectoryServices.DirectorySearcher("<query>") in
let entries = a.FindAll ()
let entries_list = 
     let (entries_seq : seq<obj>) = Seq.cast entries_list in
     Seq.toList entries_Seq
in
entries_list (* list of AD objects found from query, has type obj *)
Run Code Online (Sandbox Code Playgroud)

为了对entries_list做任何有用的事情,我必须这样做:

entries_list :?> SearchResult
Run Code Online (Sandbox Code Playgroud)

试图将它推广到seq <'a>失败,因为编译器仍然要求我静态地键入其枚举器(这是有道理的).

有没有办法避免这种情况?我开始认为这是以功能方式使用.NET数据结构的限制.

对不起,如果这是一个新手问题; 我对F#和一般的函数式编程都很环保(我喜欢它!).干杯!

  • 卡洛斯.

kvb*_*kvb 12

正如Daniel所说,您通常不需要使用,Seq.cast因为大多数集合已经实现了通用seq<'t>接口.但是,有几种.NET集合类型是在.NET 2.0中引入泛型之前构建的,它只实现了非泛型IEnumerable接口.F#编译器实际上在for循环中有一些称为"可枚举提取"的特殊逻辑,以便更容易地对付这些类型的集合.因此,如果您只处理其中一种集合类型(例如,您正在处理DirectoryServices.SearchResultCollections很多),那么简单地创建一个简单的辅助函数可能是有意义的:

let typedSearchResults (s:SearchResultCollection) =
    seq { for result in s -> result }
Run Code Online (Sandbox Code Playgroud)

然后您可以使用它而不是Seq.cast此特定集合类型.

如果您在同一个项目中使用了许多不同的旧式集合,那么您可以使用一些花哨的F#功能来制作一个通用的Seq.cast替代方案:

module Seq =
    let inline inferCast s = 
        // constrain ^t to have an Item indexed property (which we don't actually invoke)
        let _ = fun x -> (^t : (member Item : int -> ^v with get) (x, 0))
        let e = (^t : (member GetEnumerator : unit -> ^e) s)
        seq { while (^e : (member MoveNext : unit -> bool) e) do
                yield (^e : (member Current : obj) e) :?> ^v }
Run Code Online (Sandbox Code Playgroud)

现在您可以使用Seq.inferCast而不是Seq.cast,并将为您推断出正确的项目类型.不过,这可能在你的情况下有点过分了.