mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
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:
parent
df0f00bd9e
commit
28ce315d29
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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<int, int> addOne = x => x + 1;
|
||||
/// Func<int, int> 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<int, int> addOne = x => x + 1;
|
||||
/// Func<int, int> 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<int, int, int> 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<int, int, int, int> 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<int, Func<int, int>> 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<T>
|
||||
/// </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
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user