下午好,全部!
所以我一直在讨论将.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 *)
为了对entries_list做任何有用的事情,我必须这样做:
entries_list :?> SearchResult
尝试将其概括为seq&lt;'a&gt;失败,因为编译器仍然要求我静态地键入其枚举器(这是有意义的)。
有没有办法避免这种情况?我开始认为这是以功能方式使用.NET数据结构的限制。
很抱歉,如果这是一个新手问题;我对F#和一般的函数式编程都很环保(我喜欢它!)。干杯!
答案 0 :(得分:11)
正如Daniel所说,您通常不需要使用Seq.cast
,因为大多数集合已经实现了通用的seq<'t>
接口。但是,有几种.NET集合类型是在.NET 2.0中引入泛型之前构建的,它只实现了非泛型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
,并为您推断出正确的项目类型。不过,这可能在你的情况下有点过分了。
答案 1 :(得分:3)
大多数.NET集合实现IEnumerable<T>
(在F#中别名为seq<'T>
,但偶尔会遇到只实现非泛型接口IEnumerable
的.NET集合。 SearchResultCollection
就是这样一种类型。您可以使用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