From b4c1096b10a2eccee43ea087c7253fbcf2731b87 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sat, 31 Jan 2026 09:06:10 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20=E6=B7=BB=E5=8A=A0=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E5=BC=8F=E7=BC=96=E7=A8=8B=E7=AE=A1=E9=81=93=E6=89=A9?= =?UTF-8?q?=E5=B1=95=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现 Pipe 方法用于将值传递给函数 - 添加 Then 和 After 方法用于函数组合操作 - 提供 Tap 和 Also 方法用于执行副作用操作 - 添加 Map、Filter、Reduce 方法用于集合操作 - 实现 Curry、Uncurry、Partial 方法支持柯里化 - 添加 Match 和 MatchOrDefault 方法用于模式匹配 - 提供 If、IfElse 条件执行功能 - 添加类型转换相关方法 As、Cast - 实现 Let、TakeIf、TakeUnless 等 方法 - 添加 Repeat 方法用于重复执行函数 - 提供 Try 方法用于安全执行函数 - 实现 Memoize 方法用于函数结果缓存 --- .../functional/pipe/PipeExtensions.cs | 380 ++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 GFramework.Core/functional/pipe/PipeExtensions.cs diff --git a/GFramework.Core/functional/pipe/PipeExtensions.cs b/GFramework.Core/functional/pipe/PipeExtensions.cs new file mode 100644 index 0000000..3ce37ad --- /dev/null +++ b/GFramework.Core/functional/pipe/PipeExtensions.cs @@ -0,0 +1,380 @@ +// Copyright (c) 2025 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +namespace GFramework.Core.functional.pipe; + +/// +/// 提供函数式编程中的管道和组合操作扩展方法 +/// +public static class PipeExtensions +{ + /// + /// Pipe:把值送进函数(value.Pipe(func)) + /// + /// 输入值的类型 + /// 函数返回结果的类型 + /// 要传递给函数的输入值 + /// 接收输入值并返回结果的函数 + /// 函数执行后的结果 + public static TResult Pipe( + this TSource value, + Func func) + => func(value); + + /// + /// Compose:函数组合(f1.Then(f2)) + /// + /// 第一个函数的输入类型 + /// 第一个函数的输出类型,也是第二个函数的输入类型 + /// 第二个函数的输出类型 + /// 第一个要执行的函数 + /// 第二个要执行的函数 + /// 组合后的新函数,先执行first再执行second + public static Func Then( + this Func first, + Func second) + => x => second(first(x)); + + /// + /// Compose:反向组合(f2.After(f1)) + /// + /// 第一个函数的输入类型 + /// 第一个函数的输出类型,也是第二个函数的输入类型 + /// 第二个函数的输出类型 + /// 第二个要执行的函数 + /// 第一个要执行的函数 + /// 组合后的新函数,先执行first再执行second + public static Func After( + this Func second, + Func first) + => x => second(first(x)); + + /// + /// Tap:执行副作用操作但返回原值(用于调试、日志等) + /// + /// 输入值的类型 + /// 要执行操作的输入值 + /// 要执行的副作用操作 + /// 原始输入值 + public static TSource Tap( + this TSource value, + Action action) + { + action(value); + return value; + } + + /// + /// Map:对集合中的每个元素应用函数 + /// + /// 源集合元素的类型 + /// 映射后集合元素的类型 + /// 要映射的源集合 + /// 用于转换元素的函数 + /// 映射后的元素序列 + public static IEnumerable Map( + this IEnumerable source, + Func selector) + => source.Select(selector); + + /// + /// Filter:过滤集合 + /// + /// 集合元素的类型 + /// 要过滤的源集合 + /// 用于确定是否包含元素的条件函数 + /// 满足条件的元素序列 + public static IEnumerable Filter( + this IEnumerable source, + Func predicate) + => source.Where(predicate); + + /// + /// Reduce:将集合归约为单个值 + /// + /// 集合元素的类型 + /// 归约结果的类型 + /// 要归约的源集合 + /// 初始累加值 + /// 累加器函数 + /// 归约后的最终值 + public static TResult Reduce( + this IEnumerable source, + TResult seed, + Func accumulator) + => source.Aggregate(seed, accumulator); + + /// + /// Apply:将函数应用于值(柯里化辅助) + /// + /// 输入值的类型 + /// 函数返回结果的类型 + /// 要应用的函数 + /// 要传递给函数的输入值 + /// 函数执行后的结果 + public static TResult Apply( + this Func func, + TSource value) + => func(value); + + /// + /// Curry:将二参数函数转换为柯里化形式 + /// + /// 第一个参数的类型 + /// 第二个参数的类型 + /// 函数返回结果的类型 + /// 要柯里化的二参数函数 + /// 柯里化后的函数,接受一个参数并返回另一个函数 + public static Func> Curry( + this Func func) + => x => y => func(x, y); + + /// + /// Uncurry:将柯里化函数转换回二参数函数 + /// + /// 第一个参数的类型 + /// 第二个参数的类型 + /// 函数返回结果的类型 + /// 要取消柯里化的函数 + /// 恢复为二参数的函数 + public static Func Uncurry( + this Func> func) + => (x, y) => func(x)(y); + + /// + /// Partial:部分应用函数(固定第一个参数) + /// + /// 第一个参数的类型 + /// 第二个参数的类型 + /// 函数返回结果的类型 + /// 要部分应用的二参数函数 + /// 要固定的第一个参数值 + /// 部分应用后的函数,只接受第二个参数 + public static Func Partial( + this Func func, + T1 firstArg) + => x => func(firstArg, x); + + /// + /// Match:模式匹配(类似switch表达式) + /// + /// 输入值的类型 + /// 匹配结果的类型 + /// 要进行模式匹配的输入值 + /// 匹配案例数组,每个包含谓词和处理器 + /// 匹配到的处理结果 + /// 当没有匹配的案例时抛出 + public static TResult Match( + this TSource value, + params (Func predicate, Func handler)[] cases) + { + foreach (var (predicate, handler) in cases) + { + if (predicate(value)) + return handler(value); + } + throw new InvalidOperationException("No matching case found"); + } + + /// + /// MatchOrDefault:带默认值的模式匹配 + /// + /// 输入值的类型 + /// 匹配结果的类型 + /// 要进行模式匹配的输入值 + /// 当没有匹配案例时的默认返回值 + /// 匹配案例数组,每个包含谓词和处理器 + /// 匹配到的处理结果或默认值 + public static TResult MatchOrDefault( + this TSource value, + TResult defaultValue, + params (Func predicate, Func handler)[] cases) + { + foreach (var (predicate, handler) in cases) + { + if (predicate(value)) + return handler(value); + } + return defaultValue; + } + + /// + /// If:条件执行 + /// + /// 输入值的类型 + /// 要进行条件判断的输入值 + /// 条件判断函数 + /// 条件为真时执行的转换函数 + /// 条件为真时返回转换后的值,否则返回原值 + public static TSource If( + this TSource value, + Func predicate, + Func thenFunc) + => predicate(value) ? thenFunc(value) : value; + + /// + /// IfElse:条件分支 + /// + /// 输入值的类型 + /// 要进行条件判断的输入值 + /// 条件判断函数 + /// 条件为真时执行的转换函数 + /// 条件为假时执行的转换函数 + /// 根据条件返回相应的转换结果 + public static TSource IfElse( + this TSource value, + Func predicate, + Func thenFunc, + Func elseFunc) + => predicate(value) ? thenFunc(value) : elseFunc(value); + + /// + /// As:类型转换(安全转换) + /// + /// 源类型的泛型参数 + /// 目标类型的泛型参数 + /// 要进行类型转换的值 + /// 转换后的值,如果转换失败则返回null + public static TResult? As( + this TSource value) + where TResult : class + => value as TResult; + + /// + /// Cast:强制类型转换 + /// + /// 目标类型的泛型参数 + /// 要进行类型转换的对象 + /// 强制转换后的值 + public static TResult Cast( + this object value) + => (TResult)value; + + /// + /// Also:执行操作并返回原值 + /// + /// 输入值的类型 + /// 要执行操作的输入值 + /// 要执行的操作 + /// 原始输入值 + public static TSource Also( + this TSource value, + Action action) + { + action(value); + return value; + } + + /// + /// Let:将值转换为另一个值 + /// + /// 输入值的类型 + /// 转换结果的类型 + /// 要进行转换的输入值 + /// 用于转换值的函数 + /// 转换后的结果 + public static TResult Let( + this TSource value, + Func transform) + => transform(value); + + /// + /// TakeIf:条件返回值或null + /// + /// 输入值的类型 + /// 要进行条件判断的输入值 + /// 条件判断函数 + /// 条件为真时返回原值,否则返回null + public static TSource? TakeIf( + this TSource value, + Func predicate) + where TSource : class + => predicate(value) ? value : null; + + /// + /// TakeUnless:条件相反的TakeIf + /// + /// 输入值的类型 + /// 要进行条件判断的输入值 + /// 条件判断函数 + /// 条件为假时返回原值,否则返回null + public static TSource? TakeUnless( + this TSource value, + Func predicate) + where TSource : class + => !predicate(value) ? value : null; + + /// + /// Repeat:重复执行函数n次 + /// + /// 输入值的类型 + /// 初始输入值 + /// 重复执行的次数 + /// 要重复执行的函数 + /// 经过多次变换后的最终值 + public static TSource Repeat( + this TSource value, + int times, + Func func) + { + var result = value; + for (int i = 0; i < times; i++) + { + result = func(result); + } + return result; + } + + /// + /// Try:安全执行,捕获异常 + /// + /// 输入值的类型 + /// 函数返回结果的类型 + /// 要传递给函数的输入值 + /// 要安全执行的函数 + /// 包含执行状态、结果和错误信息的元组 + public static (bool success, TResult? result, Exception? error) Try( + this TSource value, + Func func) + { + try + { + return (true, func(value), null); + } + catch (Exception ex) + { + return (false, default, ex); + } + } + + /// + /// Memoize:缓存函数结果 + /// + /// 函数输入参数的类型 + /// 函数返回结果的类型 + /// 要缓存结果的函数 + /// 带有缓存功能的包装函数 + public static Func Memoize( + this Func func) + where TSource : notnull + { + var cache = new Dictionary(); + return x => + { + if (cache.TryGetValue(x, out var result)) + return result; + result = func(x); + cache[x] = result; + return result; + }; + } +}