代码之家  ›  专栏  ›  技术社区  ›  Joel Mueller

f相当于Enumerable.ofType<'a>

f#
  •  13
  • Joel Mueller  · 技术社区  · 14 年前

    …或者,如何根据它们实现的接口过滤一系列类?

    假设我有一系列从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”的运行时强制或类型测试涉及基于此程序点之前的信息的不确定类型。某些类型不允许运行时类型测试。需要进一步的类型注释。

    我不知道还要添加什么类型的注释。接口之间没有关系 'a #Foo 但foo的一个或多个子类实现了该接口。另外,不同的接口之间没有可以作为 A 但它们都是由foo的子类实现的。

    我热切地期待着,当你们中的一个指出我错过的显而易见的事情时,我会拍打自己的头。

    5 回复  |  直到 8 年前
        1
  •  6
  •   Brian    14 年前

    通常只添加一个“框”就足够了(例如,更改 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
  •  9
  •   Mark Seemann    10 年前

    您可以这样做:

    let foos = candidates |> Seq.filter (fun x -> x :? Foo) |> Seq.cast<Foo>
    
        3
  •  2
  •   Wallace    10 年前

    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)
       }
    
        4
  •  1
  •   Maslow    9 年前

    另一个选择是:

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

    我在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]