mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
refactor(core): 重构核心库代码结构
- 将异步扩展方法移至新的 AsyncFunctionalExtensions 类中 - 删除重复的数值扩展、对象扩展和字符串扩展方法 - 添加 Option 函数式编程类型实现 - 重命名 ResultExtensions 文件路径 - 修复 ResultExtensions 中的错误处理逻辑 - 更新命名空间以符合新的包结构
This commit is contained in:
parent
c454fa1acf
commit
df0f00bd9e
@ -1,5 +1,3 @@
|
||||
using GFramework.Core.Functional;
|
||||
|
||||
namespace GFramework.Core.extensions;
|
||||
|
||||
/// <summary>
|
||||
@ -99,132 +97,6 @@ public static class AsyncExtensions
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为任务工厂添加重试机制
|
||||
/// </summary>
|
||||
/// <typeparam name="T">任务结果类型</typeparam>
|
||||
/// <param name="taskFactory">任务工厂函数</param>
|
||||
/// <param name="maxRetries">最大重试次数</param>
|
||||
/// <param name="delay">重试间隔</param>
|
||||
/// <param name="shouldRetry">判断是否应该重试的函数,默认对所有异常重试</param>
|
||||
/// <param name="throwOriginal">当为 true 时直接抛出原始异常,否则包装为 AggregateException</param>
|
||||
/// <returns>任务结果</returns>
|
||||
/// <exception cref="ArgumentNullException">当 taskFactory 为 null 时抛出</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">当 maxRetries 小于 0 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var result = await (() => UnreliableOperation())
|
||||
/// .WithRetry(maxRetries: 3, delay: TimeSpan.FromSeconds(1));
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static async Task<T> WithRetry<T>(
|
||||
this Func<Task<T>> taskFactory,
|
||||
int maxRetries,
|
||||
TimeSpan delay,
|
||||
Func<Exception, bool>? shouldRetry = null,
|
||||
bool throwOriginal = false)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(taskFactory);
|
||||
|
||||
if (maxRetries < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(maxRetries), "最大重试次数不能为负数");
|
||||
|
||||
shouldRetry ??= _ => true;
|
||||
|
||||
for (var attempt = 0; attempt <= maxRetries; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await taskFactory();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 若还有重试机会且允许重试,则等待后继续;否则统一包装为 AggregateException 抛出
|
||||
if (attempt < maxRetries && shouldRetry(ex))
|
||||
{
|
||||
await Task.Delay(delay);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (throwOriginal)
|
||||
throw;
|
||||
|
||||
throw new AggregateException($"操作在 {attempt} 次重试后仍然失败", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 理论上不可达,仅满足编译器要求
|
||||
throw new AggregateException($"操作在 {maxRetries} 次重试后仍然失败");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全执行异步操作,将异常包装为 Result 类型
|
||||
/// </summary>
|
||||
/// <typeparam name="T">任务结果类型</typeparam>
|
||||
/// <param name="func">要执行的异步函数</param>
|
||||
/// <returns>包含结果或异常的 Result 对象</returns>
|
||||
/// <exception cref="ArgumentNullException">当 func 为 null 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var result = await (() => RiskyOperation()).TryAsync();
|
||||
/// result.Match(
|
||||
/// value => Console.WriteLine($"成功: {value}"),
|
||||
/// error => Console.WriteLine($"失败: {error.Message}")
|
||||
/// );
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static async Task<Result<T>> TryAsync<T>(this Func<Task<T>> func)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(func);
|
||||
|
||||
try
|
||||
{
|
||||
var result = await func();
|
||||
return new Result<T>(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new Result<T>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 等待所有任务完成
|
||||
/// </summary>
|
||||
/// <param name="tasks">任务集合</param>
|
||||
/// <exception cref="ArgumentNullException">当 tasks 为 null 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var tasks = new[] { Task1(), Task2(), Task3() };
|
||||
/// await tasks.WhenAll();
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static Task WhenAll(this IEnumerable<Task> tasks)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(tasks);
|
||||
return Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 等待所有任务完成并返回结果数组
|
||||
/// </summary>
|
||||
/// <typeparam name="T">任务结果类型</typeparam>
|
||||
/// <param name="tasks">任务集合</param>
|
||||
/// <returns>所有任务的结果数组</returns>
|
||||
/// <exception cref="ArgumentNullException">当 tasks 为 null 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var tasks = new[] { GetValue1(), GetValue2(), GetValue3() };
|
||||
/// var results = await tasks.WhenAll();
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static Task<T[]> WhenAll<T>(this IEnumerable<Task<T>> tasks)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(tasks);
|
||||
return Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为任务添加失败回退机制
|
||||
/// </summary>
|
||||
|
||||
@ -5,36 +5,6 @@ namespace GFramework.Core.extensions;
|
||||
/// </summary>
|
||||
public static class NumericExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 将值限制在指定的范围内
|
||||
/// </summary>
|
||||
/// <typeparam name="T">实现 IComparable 的类型</typeparam>
|
||||
/// <param name="value">要限制的值</param>
|
||||
/// <param name="min">最小值</param>
|
||||
/// <param name="max">最大值</param>
|
||||
/// <returns>限制后的值</returns>
|
||||
/// <exception cref="ArgumentNullException">当 value、min 或 max 为 null 时抛出</exception>
|
||||
/// <exception cref="ArgumentException">当 min 大于 max 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var value = 150;
|
||||
/// var clamped = value.Clamp(0, 100); // 返回 100
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static T Clamp<T>(this T value, T min, T max) where T : IComparable<T>
|
||||
{
|
||||
if (min.CompareTo(max) > 0)
|
||||
throw new ArgumentException($"最小值 ({min}) 不能大于最大值 ({max})");
|
||||
|
||||
if (value.CompareTo(min) < 0)
|
||||
return min;
|
||||
|
||||
if (value.CompareTo(max) > 0)
|
||||
return max;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查值是否在指定范围内
|
||||
/// </summary>
|
||||
|
||||
@ -136,27 +136,6 @@ public static class ObjectExtensions
|
||||
return obj as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对对象执行指定操作后返回对象本身,
|
||||
/// 用于构建流式(链式)调用。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">对象类型</typeparam>
|
||||
/// <param name="obj">源对象</param>
|
||||
/// <param name="action">要执行的操作</param>
|
||||
/// <returns>原对象</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// obj.As<MyRule>()
|
||||
/// ?.Do(r => r.Initialize())
|
||||
/// ?.Do(r => r.Execute());
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static T Do<T>(this T obj, Action<T> action)
|
||||
{
|
||||
action(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据对象的运行时类型,依次匹配并执行对应的处理逻辑,
|
||||
/// 只会执行第一个匹配成功的处理器。
|
||||
|
||||
@ -5,38 +5,6 @@ namespace GFramework.Core.extensions;
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 指示指定的字符串是 null 还是空字符串
|
||||
/// </summary>
|
||||
/// <param name="str">要测试的字符串</param>
|
||||
/// <returns>如果 str 参数为 null 或空字符串 (""),则为 true;否则为 false</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// string? text = null;
|
||||
/// if (text.IsNullOrEmpty()) { /* ... */ }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static bool IsNullOrEmpty(this string? str)
|
||||
{
|
||||
return string.IsNullOrEmpty(str);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指示指定的字符串是 null、空还是仅由空白字符组成
|
||||
/// </summary>
|
||||
/// <param name="str">要测试的字符串</param>
|
||||
/// <returns>如果 str 参数为 null、空字符串或仅包含空白字符,则为 true;否则为 false</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// string? text = " ";
|
||||
/// if (text.IsNullOrWhiteSpace()) { /* ... */ }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static bool IsNullOrWhiteSpace(this string? str)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(str);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 如果字符串为空,则返回 null;否则返回原字符串
|
||||
/// </summary>
|
||||
@ -103,4 +71,4 @@ public static class StringExtensions
|
||||
|
||||
return string.Join(separator, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
316
GFramework.Core/functional/Option.cs
Normal file
316
GFramework.Core/functional/Option.cs
Normal file
@ -0,0 +1,316 @@
|
||||
// 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;
|
||||
|
||||
/// <summary>
|
||||
/// 表示可能存在或不存在的值,用于替代 null 引用的函数式编程类型
|
||||
/// </summary>
|
||||
/// <typeparam name="T">值的类型</typeparam>
|
||||
public readonly struct Option<T>
|
||||
{
|
||||
private readonly T _value;
|
||||
private readonly bool _isSome;
|
||||
|
||||
/// <summary>
|
||||
/// 私有构造函数,用于创建 Some 状态
|
||||
/// </summary>
|
||||
private Option(T value)
|
||||
{
|
||||
_value = value;
|
||||
_isSome = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否有值
|
||||
/// </summary>
|
||||
public bool IsSome => _isSome;
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否无值
|
||||
/// </summary>
|
||||
public bool IsNone => !_isSome;
|
||||
|
||||
#region 工厂方法
|
||||
|
||||
/// <summary>
|
||||
/// 创建包含值的 Option
|
||||
/// </summary>
|
||||
/// <param name="value">要包装的值</param>
|
||||
/// <returns>包含值的 Option</returns>
|
||||
/// <exception cref="ArgumentNullException">当 value 为 null 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var option = Option<int>.Some(42);
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static Option<T> Some(T value)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
return new Option<T>(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示无值的 Option
|
||||
/// </summary>
|
||||
public static Option<T> None => default;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 取值
|
||||
|
||||
/// <summary>
|
||||
/// 获取值,如果无值则返回默认值
|
||||
/// </summary>
|
||||
/// <param name="defaultValue">无值时返回的默认值</param>
|
||||
/// <returns>Option 中的值或默认值</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var value = option.GetOrElse(0); // 如果无值则返回 0
|
||||
/// </code>
|
||||
/// </example>
|
||||
public T GetOrElse(T defaultValue) => _isSome ? _value : defaultValue;
|
||||
|
||||
/// <summary>
|
||||
/// 获取值,如果无值则通过工厂函数生成默认值
|
||||
/// </summary>
|
||||
/// <param name="factory">生成默认值的工厂函数</param>
|
||||
/// <returns>Option 中的值或工厂函数生成的值</returns>
|
||||
/// <exception cref="ArgumentNullException">当 factory 为 null 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var value = option.GetOrElse(() => ExpensiveDefault());
|
||||
/// </code>
|
||||
/// </example>
|
||||
public T GetOrElse(Func<T> factory)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(factory);
|
||||
return _isSome ? _value : factory();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 变换
|
||||
|
||||
/// <summary>
|
||||
/// 映射值到新类型,如果无值则返回 None
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">映射后的类型</typeparam>
|
||||
/// <param name="mapper">映射函数</param>
|
||||
/// <returns>映射后的 Option</returns>
|
||||
/// <exception cref="ArgumentNullException">当 mapper 为 null 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var option = Option<int>.Some(42);
|
||||
/// var mapped = option.Map(x => x.ToString()); // Option<string>.Some("42")
|
||||
/// </code>
|
||||
/// </example>
|
||||
public Option<TResult> Map<TResult>(Func<T, TResult> mapper)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(mapper);
|
||||
return _isSome ? Option<TResult>.Some(mapper(_value)) : Option<TResult>.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单子绑定操作,将 Option 链式转换
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">绑定后的类型</typeparam>
|
||||
/// <param name="binder">绑定函数</param>
|
||||
/// <returns>绑定后的 Option</returns>
|
||||
/// <exception cref="ArgumentNullException">当 binder 为 null 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var option = Option<string>.Some("42");
|
||||
/// var bound = option.Bind(s => int.TryParse(s, out var i)
|
||||
/// ? Option<int>.Some(i)
|
||||
/// : Option<int>.None);
|
||||
/// </code>
|
||||
/// </example>
|
||||
public Option<TResult> Bind<TResult>(Func<T, Option<TResult>> binder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(binder);
|
||||
return _isSome ? binder(_value) : Option<TResult>.None;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 过滤
|
||||
|
||||
/// <summary>
|
||||
/// 根据条件过滤值,不满足条件则返回 None
|
||||
/// </summary>
|
||||
/// <param name="predicate">过滤条件</param>
|
||||
/// <returns>满足条件的 Option 或 None</returns>
|
||||
/// <exception cref="ArgumentNullException">当 predicate 为 null 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var option = Option<int>.Some(42);
|
||||
/// var filtered = option.Filter(x => x > 0); // Option<int>.Some(42)
|
||||
/// var filtered2 = option.Filter(x => x < 0); // Option<int>.None
|
||||
/// </code>
|
||||
/// </example>
|
||||
public Option<T> Filter(Func<T, bool> predicate)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(predicate);
|
||||
return _isSome && predicate(_value) ? this : None;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 模式匹配
|
||||
|
||||
/// <summary>
|
||||
/// 模式匹配,根据是否有值执行不同的函数
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">返回值类型</typeparam>
|
||||
/// <param name="some">有值时执行的函数</param>
|
||||
/// <param name="none">无值时执行的函数</param>
|
||||
/// <returns>匹配结果</returns>
|
||||
/// <exception cref="ArgumentNullException">当 some 或 none 为 null 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var result = option.Match(
|
||||
/// some: value => $"Value: {value}",
|
||||
/// none: () => "No value"
|
||||
/// );
|
||||
/// </code>
|
||||
/// </example>
|
||||
public TResult Match<TResult>(Func<T, TResult> some, Func<TResult> none)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(some);
|
||||
ArgumentNullException.ThrowIfNull(none);
|
||||
return _isSome ? some(_value) : none();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模式匹配(副作用版本),根据是否有值执行不同的操作
|
||||
/// </summary>
|
||||
/// <param name="some">有值时执行的操作</param>
|
||||
/// <param name="none">无值时执行的操作</param>
|
||||
/// <exception cref="ArgumentNullException">当 some 或 none 为 null 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// option.Match(
|
||||
/// some: value => Console.WriteLine($"Value: {value}"),
|
||||
/// none: () => Console.WriteLine("No value")
|
||||
/// );
|
||||
/// </code>
|
||||
/// </example>
|
||||
public void Match(Action<T> some, Action none)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(some);
|
||||
ArgumentNullException.ThrowIfNull(none);
|
||||
if (_isSome)
|
||||
some(_value);
|
||||
else
|
||||
none();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 转换
|
||||
|
||||
/// <summary>
|
||||
/// 转换为 Result 类型
|
||||
/// </summary>
|
||||
/// <param name="errorMessage">无值时的错误消息</param>
|
||||
/// <returns>Result 类型</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var result = option.ToResult("Value not found");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public Result<T> ToResult(string errorMessage = "Value is None")
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(errorMessage);
|
||||
return _isSome
|
||||
? Result<T>.Succeed(_value)
|
||||
: Result<T>.Fail(new InvalidOperationException(errorMessage));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换为可枚举集合(有值时包含一个元素,无值时为空集合)
|
||||
/// </summary>
|
||||
/// <returns>可枚举集合</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var items = option.ToEnumerable(); // 有值时: [value], 无值时: []
|
||||
/// </code>
|
||||
/// </example>
|
||||
public IEnumerable<T> ToEnumerable()
|
||||
{
|
||||
if (_isSome)
|
||||
yield return _value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 隐式转换
|
||||
|
||||
/// <summary>
|
||||
/// 从值隐式转换为 Option
|
||||
/// </summary>
|
||||
public static implicit operator Option<T>(T value) =>
|
||||
value is not null ? Some(value) : None;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 相等性和比较
|
||||
|
||||
/// <summary>
|
||||
/// 判断两个 Option 是否相等
|
||||
/// </summary>
|
||||
public bool Equals(Option<T> other)
|
||||
{
|
||||
if (_isSome != other._isSome)
|
||||
return false;
|
||||
|
||||
return !_isSome || EqualityComparer<T>.Default.Equals(_value, other._value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断与对象是否相等
|
||||
/// </summary>
|
||||
public override bool Equals(object? obj) =>
|
||||
obj is Option<T> other && Equals(other);
|
||||
|
||||
/// <summary>
|
||||
/// 获取哈希码
|
||||
/// </summary>
|
||||
public override int GetHashCode() =>
|
||||
_isSome ? EqualityComparer<T>.Default.GetHashCode(_value!) : 0;
|
||||
|
||||
/// <summary>
|
||||
/// 相等运算符
|
||||
/// </summary>
|
||||
public static bool operator ==(Option<T> left, Option<T> right) =>
|
||||
left.Equals(right);
|
||||
|
||||
/// <summary>
|
||||
/// 不等运算符
|
||||
/// </summary>
|
||||
public static bool operator !=(Option<T> left, Option<T> right) =>
|
||||
!left.Equals(right);
|
||||
|
||||
#endregion
|
||||
|
||||
#region 字符串表示
|
||||
|
||||
/// <summary>
|
||||
/// 获取字符串表示
|
||||
/// </summary>
|
||||
public override string ToString() =>
|
||||
_isSome ? $"Some({_value})" : "None";
|
||||
|
||||
#endregion
|
||||
}
|
||||
110
GFramework.Core/functional/async/AsyncFunctionalExtensions.cs
Normal file
110
GFramework.Core/functional/async/AsyncFunctionalExtensions.cs
Normal file
@ -0,0 +1,110 @@
|
||||
// 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.Async;
|
||||
|
||||
/// <summary>
|
||||
/// 异步函数式编程扩展方法
|
||||
/// </summary>
|
||||
public static class AsyncFunctionalExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 为任务工厂添加重试机制
|
||||
/// </summary>
|
||||
/// <typeparam name="T">任务结果类型</typeparam>
|
||||
/// <param name="taskFactory">任务工厂函数</param>
|
||||
/// <param name="maxRetries">最大重试次数</param>
|
||||
/// <param name="delay">重试间隔</param>
|
||||
/// <param name="shouldRetry">判断是否应该重试的函数,默认对所有异常重试</param>
|
||||
/// <param name="throwOriginal">当为 true 时直接抛出原始异常,否则包装为 AggregateException</param>
|
||||
/// <returns>任务结果</returns>
|
||||
/// <exception cref="ArgumentNullException">当 taskFactory 为 null 时抛出</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">当 maxRetries 小于 0 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var result = await (() => UnreliableOperation())
|
||||
/// .WithRetry(maxRetries: 3, delay: TimeSpan.FromSeconds(1));
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static async Task<T> WithRetry<T>(
|
||||
this Func<Task<T>> taskFactory,
|
||||
int maxRetries,
|
||||
TimeSpan delay,
|
||||
Func<Exception, bool>? shouldRetry = null,
|
||||
bool throwOriginal = false)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(taskFactory);
|
||||
|
||||
if (maxRetries < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(maxRetries), "最大重试次数不能为负数");
|
||||
|
||||
shouldRetry ??= _ => true;
|
||||
|
||||
for (var attempt = 0; attempt <= maxRetries; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await taskFactory();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 若还有重试机会且允许重试,则等待后继续;否则统一包装为 AggregateException 抛出
|
||||
if (attempt < maxRetries && shouldRetry(ex))
|
||||
{
|
||||
await Task.Delay(delay);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (throwOriginal)
|
||||
throw;
|
||||
|
||||
throw new AggregateException($"操作在 {attempt} 次重试后仍然失败", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 理论上不可达,仅满足编译器要求
|
||||
throw new AggregateException($"操作在 {maxRetries} 次重试后仍然失败");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全执行异步操作,将异常包装为 Result 类型
|
||||
/// </summary>
|
||||
/// <typeparam name="T">任务结果类型</typeparam>
|
||||
/// <param name="func">要执行的异步函数</param>
|
||||
/// <returns>包含结果或异常的 Result 对象</returns>
|
||||
/// <exception cref="ArgumentNullException">当 func 为 null 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var result = await (() => RiskyOperation()).TryAsync();
|
||||
/// result.Match(
|
||||
/// value => Console.WriteLine($"成功: {value}"),
|
||||
/// error => Console.WriteLine($"失败: {error.Message}")
|
||||
/// );
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static async Task<Result<T>> TryAsync<T>(this Func<Task<T>> func)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(func);
|
||||
|
||||
try
|
||||
{
|
||||
var result = await func();
|
||||
return new Result<T>(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new Result<T>(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,18 +2,19 @@
|
||||
// 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.
|
||||
|
||||
|
||||
using GFramework.Core.Functional;
|
||||
|
||||
namespace GFramework.Core.Extensions;
|
||||
namespace GFramework.Core.functional.result;
|
||||
|
||||
/// <summary>
|
||||
/// Result 类型的扩展方法,提供函数式编程支持
|
||||
@ -46,11 +47,8 @@ public static class ResultExtensions
|
||||
if (result.IsFaulted)
|
||||
return Result<List<T>>.Fail(result.Exception);
|
||||
|
||||
if (result.IsBottom)
|
||||
return Result<List<T>>.Fail(new InvalidOperationException("Cannot combine Bottom results"));
|
||||
|
||||
if (result.IsSuccess)
|
||||
result.IfSucc(values.Add);
|
||||
values.Add(result.Match(succ: v => v, fail: _ => throw new InvalidOperationException()));
|
||||
}
|
||||
|
||||
return Result<List<T>>.Succeed(values);
|
||||
@ -265,7 +263,7 @@ public static class ResultExtensions
|
||||
string errorMessage = "Value is null") where T : class =>
|
||||
value is not null
|
||||
? Result<T>.Succeed(value)
|
||||
: Result<T>.Fail(new ArgumentNullException(nameof(value), errorMessage));
|
||||
: Result<T>.Fail(new ArgumentNullException(errorMessage));
|
||||
|
||||
/// <summary>
|
||||
/// 将可空值类型转换为 Result
|
||||
@ -275,7 +273,7 @@ public static class ResultExtensions
|
||||
string errorMessage = "Value is null") where T : struct =>
|
||||
value.HasValue
|
||||
? Result<T>.Succeed(value.Value)
|
||||
: Result<T>.Fail(new ArgumentNullException(nameof(value), errorMessage));
|
||||
: Result<T>.Fail(new ArgumentNullException(errorMessage));
|
||||
|
||||
#endregion
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user