You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现可指定区分联合类型分支的泛型筛选函数?

泛型筛选区分联合类型列表的实现方案

首先回顾你的基础代码:

定义区分联合类型

type MyUnionType =
    | TInt of int
    | TString of string
    | TBool of bool

待筛选的联合类型列表

let myUnionList = [
    TInt 1
    TString "1"
    TInt 3
    TBool false
    TBool false
    TInt 6
    TString "one"
    TString "two"
    TBool true
    TInt 9
]

原筛选方式

let filtered =
    myUnionList
    |> List.choose (fun i -> match i with | TString s -> Some s | _ -> None)
// filtered = [ "1"; "one"; "two" ]

核心结论

可以编写泛型函数实现需求,区分联合类型没有限制,关键是不能直接传递联合分支(分支不是一等公民),但可以传递「提取分支值的函数」作为参数。

具体实现方式

1. 传入提取函数(最直接的函数式方案)

定义一个泛型筛选函数,接收一个从联合类型到可选值的提取逻辑:

let filterUnion (extract: MyUnionType -> 'a option) list =
    list |> List.choose extract

使用时只需传入对应分支的提取函数:

// 提取TString分支的函数
let extractString = function | TString s -> Some s | _ -> None
let filteredStrings = filterUnion extractString myUnionList
// 结果:[ "1"; "one"; "two" ]

// 提取TInt分支的函数
let extractInt = function | TInt i -> Some i | _ -> None
let filteredInts = filterUnion extractInt myUnionList
// 结果:[1; 3; 6; 9]

2. 活动模式封装(更优雅的语法)

可以为每个分支定义活动模式,再结合泛型函数使用:

// 为各分支定义提取用的活动模式
let (|ExtractTInt|_|) = function | TInt i -> Some i | _ -> None
let (|ExtractTString|_|) = function | TString s -> Some s | _ -> None
let (|ExtractTBool|_|) = function | TBool b -> Some b | _ -> None

// 泛型筛选函数
let filterWithPattern extract list = list |> List.choose extract

// 使用示例
let filteredStrings = filterWithPattern (fun x -> match x with | ExtractTString s -> Some s | _ -> None) myUnionList

3. 反射实现(不推荐)

如果需要动态传入分支名称(如字符串"TString"),可以用反射,但此方案性能差且类型不安全,仅作参考:

open System.Reflection

let filterByUnionCaseName<'a> (caseName: string) (list: MyUnionType list) =
    let unionType = typeof<MyUnionType>
    let caseInfo = unionType.GetTypeInfo().DeclaredNestedTypes 
                   |> Seq.find (fun t -> t.Name = caseName)
    let extractMethod = caseInfo.GetMethod("op_Implicit", BindingFlags.Public ||| BindingFlags.Static)
    
    list
    |> List.choose (fun u ->
        let currentCase = FSharpValue.GetUnionFields(u, unionType) |> fst
        if currentCase.Name = caseName then
            Some (extractMethod.Invoke(null, [|u|]) :?> 'a)
        else
            None)

// 使用示例
let filteredStrings = filterByUnionCaseName<string> "TString" myUnionList

内容的提问来源于stack exchange,提问作者rasx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 12:02:05