feat(functional): 添加函数式编程扩展方法

- 在 ControlExtensions 中添加 TakeIfValue 和 TakeUnlessValue 方法,支持值类型的条件判断
- 在 ControlExtensions 中添加 When 方法,支持条件执行操作
- 在 ControlExtensions 中添加 RepeatUntil 方法,支持重复执行直到条件满足
- 在 ControlExtensions 中添加 Retry 方法,支持同步重试机制
- 在 FunctionExtensions 中添加 Compose 和 AndThen 方法,支持函数组合
- 在 FunctionExtensions 中添加 Curry 和 Uncurry 方法,支持函数柯里化
- 在 FunctionExtensions 中添加 Defer 和 Once 方法,支持延迟执行和单次执行
- 在 PipeExtensions 中添加 Tap 方法,作为 Also 的别名
- 在 PipeExtensions 中添加 Pipe 方法,支持管道操作
- 在 PipeExtensions 中添加 Let 方法,支持作用域函数
- 在 PipeExtensions 中添加 PipeIf 方法,支持条件管道操作
This commit is contained in:
GeWuYou 2026-02-26 11:20:11 +08:00 committed by gewuyou
parent df0f00bd9e
commit 28ce315d29
3 changed files with 468 additions and 0 deletions

View File

@ -47,4 +47,179 @@ public static class ControlExtensions
{
return !predicate(value) ? value : null;
}
/// <summary>
/// TakeIfValue值类型版本的 TakeIf返回 Nullable
/// </summary>
/// <typeparam name="TSource">值类型</typeparam>
/// <param name="value">要进行条件判断的输入值</param>
/// <param name="predicate">条件判断函数</param>
/// <returns>条件为真时返回原值,否则返回 null</returns>
/// <exception cref="ArgumentNullException">当 predicate 为 null 时抛出</exception>
/// <example>
/// <code>
/// var result = 42.TakeIfValue(x => x > 0); // 42
/// var result2 = -5.TakeIfValue(x => x > 0); // null
/// </code>
/// </example>
public static TSource? TakeIfValue<TSource>(
this TSource value,
Func<TSource, bool> predicate)
where TSource : struct
{
ArgumentNullException.ThrowIfNull(predicate);
return predicate(value) ? value : null;
}
/// <summary>
/// TakeUnlessValue值类型版本的 TakeUnless返回 Nullable
/// </summary>
/// <typeparam name="TSource">值类型</typeparam>
/// <param name="value">要进行条件判断的输入值</param>
/// <param name="predicate">条件判断函数</param>
/// <returns>条件为假时返回原值,否则返回 null</returns>
/// <exception cref="ArgumentNullException">当 predicate 为 null 时抛出</exception>
/// <example>
/// <code><![CDATA[
/// var result = 42.TakeUnlessValue(x => x < 0); // 42
/// var result2 = -5.TakeUnlessValue(x => x < 0); // null
/// ]]></code>
/// </example>
public static TSource? TakeUnlessValue<TSource>(
this TSource value,
Func<TSource, bool> predicate)
where TSource : struct
{
ArgumentNullException.ThrowIfNull(predicate);
return !predicate(value) ? value : null;
}
/// <summary>
/// When条件执行满足条件时执行操作并返回原值
/// </summary>
/// <typeparam name="TSource">值的类型</typeparam>
/// <param name="value">输入值</param>
/// <param name="predicate">条件判断函数</param>
/// <param name="action">满足条件时执行的操作</param>
/// <returns>原始值</returns>
/// <exception cref="ArgumentNullException">当 predicate 或 action 为 null 时抛出</exception>
/// <example>
/// <code>
/// var result = 42
/// .When(x => x > 0, x => Console.WriteLine($"Positive: {x}"))
/// .When(x => x % 2 == 0, x => Console.WriteLine("Even"));
/// </code>
/// </example>
public static TSource When<TSource>(
this TSource value,
Func<TSource, bool> predicate,
Action<TSource> action)
{
ArgumentNullException.ThrowIfNull(predicate);
ArgumentNullException.ThrowIfNull(action);
if (predicate(value))
action(value);
return value;
}
/// <summary>
/// RepeatUntil重复执行函数直到条件满足
/// </summary>
/// <typeparam name="TSource">值的类型</typeparam>
/// <param name="value">初始值</param>
/// <param name="func">每次迭代执行的函数</param>
/// <param name="predicate">停止条件</param>
/// <param name="maxIterations">最大迭代次数(防止无限循环)</param>
/// <returns>满足条件时的值</returns>
/// <exception cref="ArgumentNullException">当 func 或 predicate 为 null 时抛出</exception>
/// <exception cref="ArgumentOutOfRangeException">当 maxIterations 小于 1 时抛出</exception>
/// <exception cref="InvalidOperationException">当达到最大迭代次数仍未满足条件时抛出</exception>
/// <example>
/// <code>
/// var result = 1.RepeatUntil(
/// x => x * 2,
/// x => x >= 100,
/// maxIterations: 10
/// ); // 128
/// </code>
/// </example>
public static TSource RepeatUntil<TSource>(
this TSource value,
Func<TSource, TSource> func,
Func<TSource, bool> predicate,
int maxIterations = 1000)
{
ArgumentNullException.ThrowIfNull(func);
ArgumentNullException.ThrowIfNull(predicate);
ArgumentOutOfRangeException.ThrowIfLessThan(maxIterations, 1);
var current = value;
var iterations = 0;
while (!predicate(current))
{
if (iterations >= maxIterations)
throw new InvalidOperationException(
$"RepeatUntil 达到最大迭代次数 {maxIterations} 但条件仍未满足");
current = func(current);
iterations++;
}
return current;
}
/// <summary>
/// Retry同步重试机制失败时重试指定次数
/// </summary>
/// <typeparam name="TResult">返回值类型</typeparam>
/// <param name="func">要执行的函数</param>
/// <param name="maxRetries">最大重试次数</param>
/// <param name="delayMilliseconds">每次重试之间的延迟(毫秒)</param>
/// <returns>函数执行结果</returns>
/// <exception cref="ArgumentNullException">当 func 为 null 时抛出</exception>
/// <exception cref="ArgumentOutOfRangeException">当 maxRetries 小于 0 或 delayMilliseconds 小于 0 时抛出</exception>
/// <exception cref="AggregateException">当所有重试都失败时抛出,包含所有异常</exception>
/// <example>
/// <code>
/// var result = ControlExtensions.Retry(
/// () => UnstableOperation(),
/// maxRetries: 3,
/// delayMilliseconds: 100
/// );
/// </code>
/// </example>
public static TResult Retry<TResult>(
Func<TResult> func,
int maxRetries = 3,
int delayMilliseconds = 0)
{
ArgumentNullException.ThrowIfNull(func);
ArgumentOutOfRangeException.ThrowIfNegative(maxRetries);
ArgumentOutOfRangeException.ThrowIfNegative(delayMilliseconds);
var exceptions = new List<Exception>();
var attempts = maxRetries + 1;
for (var i = 0; i < attempts; i++)
{
try
{
return func();
}
catch (Exception ex)
{
exceptions.Add(ex);
if (i < maxRetries && delayMilliseconds > 0)
Thread.Sleep(delayMilliseconds);
}
}
throw new AggregateException(
$"操作在 {attempts} 次尝试后仍然失败",
exceptions);
}
}

View File

@ -99,4 +99,185 @@ public static class FunctionExtensions
}
#endregion
#region Compose & AndThen
/// <summary>
/// Compose函数组合返回 f(g(x))
/// 数学表示:(f ∘ g)(x) = f(g(x))
/// </summary>
/// <typeparam name="T">输入类型</typeparam>
/// <typeparam name="TIntermediate">中间类型</typeparam>
/// <typeparam name="TResult">输出类型</typeparam>
/// <param name="f">外层函数</param>
/// <param name="g">内层函数</param>
/// <returns>组合后的函数</returns>
/// <exception cref="ArgumentNullException">当 f 或 g 为 null 时抛出</exception>
/// <example>
/// <code>
/// Func&lt;int, int&gt; addOne = x => x + 1;
/// Func&lt;int, int&gt; multiplyTwo = x => x * 2;
/// var composed = multiplyTwo.Compose(addOne); // (x + 1) * 2
/// var result = composed(5); // (5 + 1) * 2 = 12
/// </code>
/// </example>
public static Func<T, TResult> Compose<T, TIntermediate, TResult>(
this Func<TIntermediate, TResult> f,
Func<T, TIntermediate> g)
{
ArgumentNullException.ThrowIfNull(f);
ArgumentNullException.ThrowIfNull(g);
return x => f(g(x));
}
/// <summary>
/// AndThen函数链式调用返回 g(f(x))
/// 数学表示:(f >> g)(x) = g(f(x))
/// </summary>
/// <typeparam name="T">输入类型</typeparam>
/// <typeparam name="TIntermediate">中间类型</typeparam>
/// <typeparam name="TResult">输出类型</typeparam>
/// <param name="f">第一个函数</param>
/// <param name="g">第二个函数</param>
/// <returns>链式调用后的函数</returns>
/// <exception cref="ArgumentNullException">当 f 或 g 为 null 时抛出</exception>
/// <example>
/// <code>
/// Func&lt;int, int&gt; addOne = x => x + 1;
/// Func&lt;int, int&gt; multiplyTwo = x => x * 2;
/// var chained = addOne.AndThen(multiplyTwo); // (x + 1) * 2
/// var result = chained(5); // (5 + 1) * 2 = 12
/// </code>
/// </example>
public static Func<T, TResult> AndThen<T, TIntermediate, TResult>(
this Func<T, TIntermediate> f,
Func<TIntermediate, TResult> g)
{
ArgumentNullException.ThrowIfNull(f);
ArgumentNullException.ThrowIfNull(g);
return x => g(f(x));
}
#endregion
#region Curry & Uncurry
/// <summary>
/// Curry将二参数函数柯里化为嵌套的单参数函数
/// </summary>
/// <typeparam name="T1">第一个参数类型</typeparam>
/// <typeparam name="T2">第二个参数类型</typeparam>
/// <typeparam name="TResult">返回值类型</typeparam>
/// <param name="func">要柯里化的函数</param>
/// <returns>柯里化后的函数</returns>
/// <exception cref="ArgumentNullException">当 func 为 null 时抛出</exception>
/// <example>
/// <code>
/// Func&lt;int, int, int&gt; add = (x, y) => x + y;
/// var curriedAdd = add.Curry();
/// var add5 = curriedAdd(5);
/// var result = add5(3); // 8
/// </code>
/// </example>
public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(
this Func<T1, T2, TResult> func)
{
ArgumentNullException.ThrowIfNull(func);
return arg1 => arg2 => func(arg1, arg2);
}
/// <summary>
/// Curry将三参数函数柯里化为嵌套的单参数函数
/// </summary>
/// <typeparam name="T1">第一个参数类型</typeparam>
/// <typeparam name="T2">第二个参数类型</typeparam>
/// <typeparam name="T3">第三个参数类型</typeparam>
/// <typeparam name="TResult">返回值类型</typeparam>
/// <param name="func">要柯里化的函数</param>
/// <returns>柯里化后的函数</returns>
/// <exception cref="ArgumentNullException">当 func 为 null 时抛出</exception>
/// <example>
/// <code>
/// Func&lt;int, int, int, int&gt; add3 = (x, y, z) => x + y + z;
/// var curriedAdd = add3.Curry();
/// var result = curriedAdd(1)(2)(3); // 6
/// </code>
/// </example>
public static Func<T1, Func<T2, Func<T3, TResult>>> Curry<T1, T2, T3, TResult>(
this Func<T1, T2, T3, TResult> func)
{
ArgumentNullException.ThrowIfNull(func);
return arg1 => arg2 => arg3 => func(arg1, arg2, arg3);
}
/// <summary>
/// Uncurry将柯里化的函数还原为多参数函数
/// </summary>
/// <typeparam name="T1">第一个参数类型</typeparam>
/// <typeparam name="T2">第二个参数类型</typeparam>
/// <typeparam name="TResult">返回值类型</typeparam>
/// <param name="func">柯里化的函数</param>
/// <returns>还原后的多参数函数</returns>
/// <exception cref="ArgumentNullException">当 func 为 null 时抛出</exception>
/// <example>
/// <code>
/// Func&lt;int, Func&lt;int, int&gt;&gt; curriedAdd = x => y => x + y;
/// var add = curriedAdd.Uncurry();
/// var result = add(5, 3); // 8
/// </code>
/// </example>
public static Func<T1, T2, TResult> Uncurry<T1, T2, TResult>(
this Func<T1, Func<T2, TResult>> func)
{
ArgumentNullException.ThrowIfNull(func);
return (arg1, arg2) => func(arg1)(arg2);
}
#endregion
#region Defer & Once
/// <summary>
/// Defer延迟执行函数返回 Lazy&lt;T&gt;
/// </summary>
/// <typeparam name="TResult">返回值类型</typeparam>
/// <param name="func">要延迟执行的函数</param>
/// <returns>包装了延迟执行的 Lazy 对象</returns>
/// <exception cref="ArgumentNullException">当 func 为 null 时抛出</exception>
/// <example>
/// <code>
/// var lazy = (() => ExpensiveComputation()).Defer();
/// // 此时尚未执行
/// var result = lazy.Value; // 首次访问时才执行
/// </code>
/// </example>
public static Lazy<TResult> Defer<TResult>(this Func<TResult> func)
{
ArgumentNullException.ThrowIfNull(func);
return new Lazy<TResult>(func);
}
/// <summary>
/// Once确保函数只执行一次后续调用返回缓存的结果线程安全
/// </summary>
/// <typeparam name="TResult">返回值类型</typeparam>
/// <param name="func">要执行的函数</param>
/// <returns>包装后的函数,确保只执行一次</returns>
/// <exception cref="ArgumentNullException">当 func 为 null 时抛出</exception>
/// <example>
/// <code>
/// var counter = 0;
/// var once = (() => ++counter).Once();
/// var result1 = once(); // 1
/// var result2 = once(); // 1 (不会再次执行)
/// </code>
/// </example>
public static Func<TResult> Once<TResult>(this Func<TResult> func)
{
ArgumentNullException.ThrowIfNull(func);
var lazy = new Lazy<TResult>(func, LazyThreadSafetyMode.ExecutionAndPublication);
return () => lazy.Value;
}
#endregion
}

View File

@ -30,4 +30,116 @@ public static class PipeExtensions
action(value);
return value;
}
/// <summary>
/// Tap
/// Also 的别名,对值执行副作用操作并返回原值
/// 提供更符合某些编程风格的命名
/// </summary>
/// <typeparam name="T">值的类型</typeparam>
/// <param name="value">要处理的值</param>
/// <param name="action">要执行的副作用操作</param>
/// <returns>原始值</returns>
/// <exception cref="ArgumentNullException">当 action 为 null 时抛出</exception>
/// <example>
/// <code>
/// var result = GetUser()
/// .Tap(user => Console.WriteLine($"User: {user.Name}"))
/// .Tap(user => _logger.LogInfo($"Processing user {user.Id}"));
/// </code>
/// </example>
public static T Tap<T>(this T value, Action<T> action)
{
ArgumentNullException.ThrowIfNull(action);
action(value);
return value;
}
/// <summary>
/// Pipe
/// 管道操作符,将值传递给函数并返回结果
/// 用于构建流式的函数调用链
/// </summary>
/// <typeparam name="TSource">输入类型</typeparam>
/// <typeparam name="TResult">输出类型</typeparam>
/// <param name="value">输入值</param>
/// <param name="func">转换函数</param>
/// <returns>转换后的值</returns>
/// <exception cref="ArgumentNullException">当 func 为 null 时抛出</exception>
/// <example>
/// <code>
/// var result = 42
/// .Pipe(x => x * 2)
/// .Pipe(x => x.ToString())
/// .Pipe(s => $"Result: {s}");
/// </code>
/// </example>
public static TResult Pipe<TSource, TResult>(
this TSource value,
Func<TSource, TResult> func)
{
ArgumentNullException.ThrowIfNull(func);
return func(value);
}
/// <summary>
/// Let
/// 作用域函数,将值传递给函数并返回结果
/// 与 Pipe 功能相同但提供不同的语义Kotlin 风格)
/// </summary>
/// <typeparam name="TSource">输入类型</typeparam>
/// <typeparam name="TResult">输出类型</typeparam>
/// <param name="value">输入值</param>
/// <param name="transform">转换函数</param>
/// <returns>转换后的值</returns>
/// <exception cref="ArgumentNullException">当 transform 为 null 时抛出</exception>
/// <example>
/// <code>
/// var result = GetUser().Let(user => new UserDto
/// {
/// Id = user.Id,
/// Name = user.Name
/// });
/// </code>
/// </example>
public static TResult Let<TSource, TResult>(
this TSource value,
Func<TSource, TResult> transform)
{
ArgumentNullException.ThrowIfNull(transform);
return transform(value);
}
/// <summary>
/// PipeIf
/// 条件管道,根据条件选择不同的转换函数
/// </summary>
/// <typeparam name="TSource">输入类型</typeparam>
/// <typeparam name="TResult">输出类型</typeparam>
/// <param name="value">输入值</param>
/// <param name="predicate">条件判断函数</param>
/// <param name="ifTrue">条件为真时的转换函数</param>
/// <param name="ifFalse">条件为假时的转换函数</param>
/// <returns>转换后的值</returns>
/// <exception cref="ArgumentNullException">当任何参数为 null 时抛出</exception>
/// <example>
/// <code>
/// var result = 42.PipeIf(
/// x => x > 0,
/// x => $"Positive: {x}",
/// x => $"Non-positive: {x}"
/// );
/// </code>
/// </example>
public static TResult PipeIf<TSource, TResult>(
this TSource value,
Func<TSource, bool> predicate,
Func<TSource, TResult> ifTrue,
Func<TSource, TResult> ifFalse)
{
ArgumentNullException.ThrowIfNull(predicate);
ArgumentNullException.ThrowIfNull(ifTrue);
ArgumentNullException.ThrowIfNull(ifFalse);
return predicate(value) ? ifTrue(value) : ifFalse(value);
}
}