6

大家下午好!

因此,我一直在尝试将 .NET 集合转换为功能数据结构的方法。我能得到的最好的结果就是先把它转换成一个序列,然后再转换成我想要的任何东西。

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

例子:

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 *)

为了对条目列表做任何有用的事情,我必须这样做:

entries_list :?> SearchResult

尝试将其概括为 seq<'a> 失败,因为编译器仍然要求我静态键入它的枚举数(这是有道理的)。

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

对不起,如果这是一个新手问题;总的来说,我对 F# 和函数式编程很熟悉(并且很喜欢它!)。干杯!

  • 卡洛斯。
4

2 回答 2

12

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

let typedSearchResults (s:SearchResultCollection) =
    seq { for result in s -> result }

然后您可以使用它来代替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 }

现在您可以使用Seq.inferCast代替Seq.cast,并且将为您推断出正确的项目类型。不过,在您的情况下,这可能有点矫枉过正。

于 2012-11-26T21:53:06.493 回答
3

大多数 .NET 集合都实现IEnumerable<T>(与 F# 中的别名一样seq<'T>),但有时您会遇到一个仅实现非泛型接口的集合IEnumerableSearchResultCollection就是这样一种类型。您可以使用Seq.cast将这些集合转换为seq<'T>,从而可以将它们与Seq模块中的函数一起使用。

open System.DirectoryServices

use searcher = new DirectorySearcher("<query>")
let entries = searcher.FindAll() |> Seq.cast<SearchResult>
let entries_list = Seq.toList entries
于 2012-11-26T21:08:29.473 回答