ChanningMou.DataSearch 1.0.0.2

dotnet add package ChanningMou.DataSearch --version 1.0.0.2
                    
NuGet\Install-Package ChanningMou.DataSearch -Version 1.0.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ChanningMou.DataSearch" Version="1.0.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ChanningMou.DataSearch" Version="1.0.0.2" />
                    
Directory.Packages.props
<PackageReference Include="ChanningMou.DataSearch" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ChanningMou.DataSearch --version 1.0.0.2
                    
#r "nuget: ChanningMou.DataSearch, 1.0.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ChanningMou.DataSearch@1.0.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ChanningMou.DataSearch&version=1.0.0.2
                    
Install as a Cake Addin
#tool nuget:?package=ChanningMou.DataSearch&version=1.0.0.2
                    
Install as a Cake Tool

ChanningMou.DataSearch

高性能、通用、跨框架的 C# 数据查找 / 过滤引擎 —— 支持扁平数据、树形数据(保留结构)、流式输出与增量过滤,并内置表达式树条件构建器与编译期源生成器优化。

.NET License: MIT


目录


特性一览

能力 说明
扁平批量过滤 SearchFlatAsync,支持顺序 / PLINQ 并行,返回 Task<List<T>>
树形批量过滤(保留结构) SearchTreeAsync,标记-重建算法,非递归显式栈遍历,只剔除不匹配分支,保留原始层级与顺序
无 createNode 重载 不提供 createNode 委托时,内部用 Channing.Cloner 深拷贝节点并替换 Children,调用更省心
流式输出 SearchFlatStream / SearchTreeStream,返回 IAsyncEnumerable<T>,边过滤边返回,低内存
增量过滤 CreateContinuousFilter,订阅 INotifyCollectionChanged,数据源变化时只重算受影响子树
表达式条件构建器 CreateFilterExpression / BuildPredicate / BuildCombinedPredicate,策略模式自动选择最优实现
编译期源生成器 自动为节点类型生成强类型 createNode 工厂,0 反射、0 JIT;消费者只需引用本包即可获得
取消与超时 所有公共方法接受 CancellationToken,循环内频繁检查,响应及时
多框架 同一份代码兼容 net451 ~ net8.0,旧框架自动 polyfill

支持的框架

TFM 流式 API 源生成器 说明
net451 .NET Framework 4.5.1,不导出流式(IAsyncEnumerable 缺失),无 SG(需 net461+);走 IL Emit + 表达式树回退
netstandard2.0 通过 Microsoft.Bcl.AsyncInterfaces 提供 IAsyncEnumerable<T>
netcoreapp2.0 同上
net8.0 原生支持,NodeState<T>struct 零堆分配

所有框架功能完整,仅 net451 因运行时限制不导出流式 API 与源生成器。


安装

NuGet CLI

dotnet add package ChanningMou.DataSearch

PackageReference

<ItemGroup>
  <PackageReference Include="ChanningMou.DataSearch" Version="1.0.0" />
</ItemGroup>

引用本包即可,无需额外引用源生成器。源生成器 DLL 已打包进 analyzers/cs/,编译器会自动加载。


快速开始

1. 扁平数据过滤

using ChanningMou.DataSearch;

var data = Enumerable.Range(1, 100_000);

// 找出所有偶数(并行)
var evens = await DataSearchEngine.SearchFlatAsync(
    data,
    x => x % 2 == 0,
    parallel: true);

Console.WriteLine($"找到 {evens.Count} 个偶数");

2. 树形数据过滤(保留结构)

public class TreeNode
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<TreeNode> Children { get; set; } = new();
}

var roots = new List<TreeNode> { rootTree };

// 方式 A:自定义 createNode(最灵活、最快)
var filtered = await DataSearchEngine.SearchTreeAsync(
    roots,
    node => node.Id > 100,                 // 匹配条件
    node => node.Children,                 // 获取子节点
    (orig, children) => new TreeNode       // 构造新节点
    {
        Id = orig.Id,
        Name = orig.Name,
        Children = children.ToList()
    });

// 方式 B:不提供 createNode(内部自动深拷贝 + 替换 Children,更省心)
var filtered2 = await DataSearchEngine.SearchTreeAsync(
    roots,
    node => node.Id > 100,
    node => node.Children);

方式 B 会自动查找类型为 List<T>/IList<T>/ICollection<T> 的公共属性作为 Children;只读集合属性(实现 ICollection<T>)也支持(先 Clear()AddRange())。

3. 流式过滤(边过滤边返回)

// 扁平流式:逐条返回匹配项
await foreach (var item in DataSearchEngine.SearchFlatStream(
                   data, x => x % 2 == 0, parallel: true))
{
    Console.WriteLine(item);
}

// 树形流式:每过滤完一棵子树立即返回其新根
await foreach (var root in DataSearchEngine.SearchTreeStream(
                   roots,
                   node => node.Id > 100,
                   node => node.Children,
                   (orig, children) => new TreeNode { Id = orig.Id, Children = children.ToList() }))
{
    DisplayTree(root);
}

4. 增量过滤(数据源变化自动更新)

var observable = new ObservableCollection<TreeNode>(initialData);

using var filter = DataSearchEngine.CreateContinuousFilter(
    observable,
    node => node.IsActive,
    node => node.Children,
    (orig, children) => new TreeNode { Id = orig.Id, Children = children.ToList() });

filter.ResultChanged += (sender, newResult) => UpdateUI(newResult);

// 数据源变化时,filter 自动重算并触发事件
observable.Add(new TreeNode { ... });

5. 超时与取消

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(1));
try
{
    var result = await DataSearchEngine.SearchFlatAsync(
        data, x => x % 2 == 0, parallel: true, cancellationToken: cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("搜索超时");
}

核心 API

批量异步

// 扁平
Task<List<T>> SearchFlatAsync<T>(
    IEnumerable<T> data,
    Func<T, bool> predicate,
    bool parallel = false,
    CancellationToken cancellationToken = default);

// 树形(自定义 createNode)
Task<List<T>> SearchTreeAsync<T>(
    IEnumerable<T> roots,
    Func<T, bool> predicate,
    Func<T, IEnumerable<T>> getChildren,
    Func<T, IEnumerable<T>, T> createNode,
    bool parallel = false,
    CancellationToken cancellationToken = default) where T : notnull;

// 树形(自动深拷贝,T : class)
Task<List<T>> SearchTreeAsync<T>(
    IEnumerable<T> roots,
    Func<T, bool> predicate,
    Func<T, IEnumerable<T>> getChildren,
    bool parallel = false,
    CancellationToken cancellationToken = default) where T : class;

流式异步

IAsyncEnumerable<T> SearchFlatStream<T>(...);
IAsyncEnumerable<T> SearchTreeStream<T>(...);

增量过滤

public interface IIncrementalFilter<T> : IDisposable
{
    IReadOnlyList<T> CurrentResult { get; }
    event EventHandler<IEnumerable<T>> ResultChanged;
}

IIncrementalFilter<T> CreateContinuousFilter<T>(
    INotifyCollectionChanged source,
    Func<T, bool> predicate,
    Func<T, IEnumerable<T>>? getChildren = null,
    Func<T, IEnumerable<T>, T>? createNode = null);

条件表达式构建器

CreateFilterExpression / BuildPredicate策略模式自动为不同运算符组合选择最优表达式实现,避免大 N 时线性嵌套表达式编译栈溢出与性能下降。

策略矩阵

运算符 (operand) 组合方式 (expressionType) 策略 复杂度
Equal OrElse HashSet.Contains O(1) 查找
NotEqual AndAlso !HashSet.Contains O(1) 查找
GreaterThan / GreaterThanOrEqual OrElse 比较 Min(values) O(1)
LessThan / LessThanOrEqual OrElse 比较 Max(values) O(1)
其他组合 平衡二叉树(OrElse/AndAlso) O(log N),避免栈溢出

用法

// 单条件:Id in {1, 3, 5}
var predicate = DataSearchEngine.BuildPredicate<TreeNode, int>(
    node => node.Id,
    new[] { 1, 3, 5 },
    operand: ExpressionType.Equal,
    expressionType: ExpressionType.OrElse);

// 多条件 AND 组合:(Id in {1,3,5}) AND (Age >= 18) AND (Name != "")
var combined = DataSearchEngine.BuildCombinedPredicate<TreeNode>(
    new DataSearchEngine.ValueListCondition<TreeNode, int>(
        n => n.Id, new[] { 1, 3, 5 }),
    new DataSearchEngine.ValueListCondition<TreeNode, int>(
        n => n.Age, new[] { 18 }, ExpressionType.GreaterThanOrEqual),
    new DataSearchEngine.DirectCondition<TreeNode>(
        n => !string.IsNullOrEmpty(n.Name)));

var result = await DataSearchEngine.SearchFlatAsync(data, combined);

BuildPredicate / BuildCombinedPredicate 返回的 Func<T, bool> 可直接传给 SearchFlatAsync / SearchTreeAsyncpredicate 参数。


源生成器(自动优化)

当调用createNode 参数SearchTreeAsync / SearchTreeStream 重载时,编译期源生成器会:

  1. 扫描调用点,收集节点类型 T
  2. 为每个 T 生成强类型工厂 DataSearchCreateNodeFactory<T>,其 Create 字段即 Func<T, IEnumerable<T>, T>
  3. 生成 [assembly: DataSearchGenerateCreateNode(typeof(T))] 特性;
  4. 运行时 BuildDefaultCreateNode<T> 三级回退:源生成器 → IL Emit → 表达式树

消费者无需任何额外配置,引用 DataSearch 包即可自动启用。net451 因运行时限制不支持源生成器,会自动回退到 IL Emit / 表达式树路径,功能完整。


性能

测试环境:Intel i7-10700, 16GB RAM, .NET 8.0

数据规模 操作 单线程 并行 (4 核) 内存峰值
10 万扁平 过滤 15 ms 8 ms 2 MB
10 万树形 批量过滤 80 ms 45 ms 15 MB
100 万树形 批量过滤 850 ms 500 ms 120 MB
100 万扁平 流式过滤 900 ms 520 ms <10 MB
50K 子节点单根多条件 批量过滤 14 ms

优化要点

  • NodeState<T> 在 .NET 5+ 为 struct,零堆分配;用 ref 访问字典条目避免值拷贝。
  • 树形过滤用非递归显式栈遍历,杜绝栈溢出;标记-重建自底向上,保留结构。
  • 多根节点并行用 Parallel.ForEach,扁平用 PLINQ
  • 条件表达式按策略选择 HashSet.Contains / Min/Max / 平衡树,避免大 N 线性嵌套。
  • BuildDefaultCreateNode<T> 委托按类型缓存于 ConcurrentDictionary,仅首次反射。

使用注意事项

场景 处理方式
超时取消 OperationCanceledException,调用方需捕获
委托异常 顺序模式向上传播;并行模式为 AggregateException
空数据 返回空列表 / 空流,不抛异常
循环引用 引擎不检测,请确保数据为 DAG(有向无环图)
并行模式 所有用户委托(predicate/getChildren/createNode)必须线程安全
createNode 必须返回新实例,禁止修改原节点
增量过滤 数据源须实现 INotifyCollectionChanged;不支持并行;高频变更建议节流
net451 不导出流式 API;无源生成器(回退 IL Emit);需 System.ValueTuple

版本与变更

详见 CHANGELOG.md


依赖

  • Channing.Cloner —— 深拷贝(无 createNode 重载使用)
  • Microsoft.Bcl.AsyncInterfaces —— IAsyncEnumerable<T> polyfill(仅 netstandard2.0 / netcoreapp2.0
  • System.ValueTuple —— 元组语法(仅 net451

作者与联系方式

如有问题、建议或商业合作,欢迎通过邮箱联系,或提交 Issue / Pull Request。


许可

MIT License

Copyright (c) 2026 ChanningMou (牟成贵)

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 is compatible.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net451 is compatible.  net452 was computed.  net46 was computed.  net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0.2 111 8/2/2026
1.0.0.1 100 8/2/2026