F#相当于Ienumerable.OfType<'a>

时间:2010-03-26 05:11:57

标签: f#

...或者,如何通过它们实现的接口过滤一系列类?

假设我有一系列从Foo继承的对象,seq<#Foo>。换句话说,我的序列将包含Foo的四个不同子类中的一个或多个。

每个子类实现一个不同的独立接口,它与其他子类实现的接口不共享。

现在我需要将此序列过滤为仅实现特定接口的项目。

C#版本很简单:

    void MergeFoosIntoList<T>(IEnumerable<Foo> allFoos, IList<T> dest) 
        where T : class
    {
        foreach (var foo in allFoos)
        {
            var castFoo = foo as T;
            if (castFoo != null)
            {
                dest.Add(castFoo);
            }
        }
    }

我可以使用F#中的LINQ:

    let mergeFoosIntoList (foos:seq<#Foo>) (dest:IList<'a>) =
            System.Linq.Enumerable.OfType<'a>(foos)
            |> Seq.iter dest.Add

然而,我觉得应该有一种更惯用的方法来实现它。我认为这会奏效......

    let mergeFoosIntoList (foos:seq<#Foo>) (dest:IList<'a>) =
            foos
            |> Seq.choose (function | :? 'a as x -> Some(x) | _ -> None)
            |> Seq.iter dest.Add

然而,编辑抱怨:? 'a - 告诉我:

  

从“b到”a类型的运行时强制或类型测试涉及基于此程序点之前的信息的不确定类型。某些类型不允许运行时类型测试。需要进一步的类型注释。

我无法弄清楚要添加的其他类型注释。除了Foo的一个或多个子类实现该接口之外,接口'a#Foo之间没有任何关系。此外,可以作为'a传入的不同接口之间没有任何关系,除了它们都是由Foo的子类实现的。

一旦有一个善良的人指出我一直缺少的显而易见的东西,我就急切地想要把自己打到脑袋里。

5 个答案:

答案 0 :(得分:9)

你可以这样做:

let foos = candidates |> Seq.filter (fun x -> x :? Foo) |> Seq.cast<Foo>

答案 1 :(得分:6)

通常只需添加“框”就足够了(例如将function更改为fun x -> match box x with),但让我试一试......

呀;基本上你不能从一个任意泛型类型转向另一个,但你可以向上转换为System.Object(通过box),然后向下转换为你喜欢的任何类型:

type Animal() = class end
type Dog() = inherit Animal()
type Cat() = inherit Animal()

let pets : Animal list = 
    [Dog(); Cat(); Dog(); Cat(); Dog()]
printfn "%A" pets

open System.Collections.Generic     

let mergeIntoList (pets:seq<#Animal>) (dest:IList<'a>) = 
    pets 
    |> Seq.choose (fun p -> match box p with  
                            | :? 'a as x -> Some(x) | _ -> None) //'
    |> Seq.iter dest.Add 

let l = new List<Dog>()
mergeIntoList pets l
l |> Seq.iter (printfn "%A")

答案 2 :(得分:2)

来自https://gist.github.com/kos59125/3780229

let ofType<'a> (source : System.Collections.IEnumerable) : seq<'a> =
   let resultType = typeof<'a>
   seq {
      for item in source do
         match item with
            | null -> ()
            | _ ->
               if resultType.IsAssignableFrom (item.GetType ())
               then
                  yield (downcast item)
   }

答案 3 :(得分:1)

倾向于那些人的另一种选择:

Module Seq = 
    let ofType<'a> (items: _ seq)= items |> Seq.choose(fun i -> match box i with | :? 'a as a -> Some a |_ -> None)

答案 4 :(得分:0)

我在nuget上有一个开源库,FSharp.Interop.Compose

将大多数Linq方法转换为idomatic F# form。包括OfType

测试用例:

[<Fact>]
let ofType () =
   let list = System.Collections.ArrayList()
   list.Add(1) |> ignore
   list.Add("2") |> ignore
   list.Add(3) |> ignore
   list.Add("4") |> ignore
   list
       |> Enumerable.ofType<int>
       |> Seq.toList |> should equal [1;3]