// 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; /// /// Result 类型的扩展方法,提供函数式编程支持 /// public static class ResultExtensions { #region 聚合 /// /// 将多个结果合并,全部成功则返回值列表,遇到第一个失败即短路返回 /// /// /// /// var combined = new[] /// { /// Result<int>.Succeed(1), /// Result<int>.Succeed(2), /// Result<int>.Succeed(3) /// }.Combine(); // Result<List<int>> [1, 2, 3] /// /// public static Result> Combine( this IEnumerable> results) { ArgumentNullException.ThrowIfNull(results); var values = new List(); foreach (var result in results) { if (result.IsFaulted) return Result>.Fail(result.Exception); if (result.IsSuccess) values.Add(result.Match(succ: v => v, fail: _ => throw new InvalidOperationException())); } return Result>.Succeed(values); } #endregion #region 变换 /// /// 映射成功值到新类型 /// /// /// /// var result = Result<int>.Succeed(42); /// var mapped = result.Map(x => x.ToString()); // Result<string> "42" /// /// public static Result Map( this Result result, Func mapper) { ArgumentNullException.ThrowIfNull(mapper); return result.Map(mapper); } /// /// 绑定操作,将结果链式转换 /// /// /// /// var result = Result<int>.Succeed(42); /// var bound = result.Bind(x => x > 0 /// ? Result<string>.Succeed(x.ToString()) /// : Result<string>.Fail(new ArgumentException("Value must be positive"))); /// /// public static Result Bind( this Result result, Func> binder) { ArgumentNullException.ThrowIfNull(binder); return result.Bind(binder); } /// /// 异步绑定操作,将结果链式转换为异步结果 /// /// /// /// var result = Result<int>.Succeed(42); /// var bound = await result.BindAsync(async x => /// await GetUserAsync(x) is User user /// ? Result<User>.Succeed(user) /// : Result<User>.Fail(new Exception("User not found"))); /// /// public static async Task> BindAsync( this Result result, Func>> binder) { ArgumentNullException.ThrowIfNull(binder); return result.IsSuccess ? await binder(result.Match(succ: v => v, fail: _ => throw new InvalidOperationException())) : Result.Fail(result.Exception); } #endregion #region 副作用 /// /// 成功时执行副作用,返回原始结果(可链式调用) /// /// /// /// Result<int>.Succeed(42) /// .OnSuccess(x => Console.WriteLine($"Value: {x}")) /// .OnFailure(ex => Console.WriteLine($"Error: {ex.Message}")); /// /// public static Result OnSuccess( this Result result, Action action) { ArgumentNullException.ThrowIfNull(action); result.IfSucc(action); return result; } /// /// 失败时执行副作用,返回原始结果(可链式调用) /// public static Result OnFailure( this Result result, Action action) { ArgumentNullException.ThrowIfNull(action); result.IfFail(action); return result; } #endregion #region 验证 /// /// 确保成功值满足条件,否则转换为失败 /// /// /// /// var result = Result<int>.Succeed(42) /// .Ensure(x => x > 0, "Value must be positive") /// .Ensure(x => x < 100, "Value must be less than 100"); /// /// public static Result Ensure( this Result result, Func predicate, string errorMessage) { ArgumentNullException.ThrowIfNull(predicate); ArgumentException.ThrowIfNullOrWhiteSpace(errorMessage); if (result.IsFaulted) return result; return result.Match( succ: value => predicate(value) ? result : Result.Fail(new ArgumentException(errorMessage)), fail: _ => result ); } /// /// Ensure 的异常重载,可以传入更具体的异常类型 /// public static Result Ensure( this Result result, Func predicate, Func exceptionFactory) { ArgumentNullException.ThrowIfNull(predicate); ArgumentNullException.ThrowIfNull(exceptionFactory); if (result.IsFaulted) return result; return result.Match( succ: value => predicate(value) ? result : Result.Fail(exceptionFactory(value)), fail: _ => result ); } #endregion #region 安全执行 /// /// 安全执行同步委托,自动捕获异常为 Result /// /// /// /// var result = ResultExtensions.Try(() => int.Parse("42")); /// /// public static Result Try(Func func) { ArgumentNullException.ThrowIfNull(func); return Result.Try(func); } /// /// 安全执行异步委托,自动捕获异常为 Result /// /// /// /// var result = await ResultExtensions.TryAsync(async () => await GetDataAsync()); /// /// public static async Task> TryAsync(Func> func) { ArgumentNullException.ThrowIfNull(func); try { return Result.Succeed(await func()); } catch (Exception ex) { return Result.Fail(ex); } } #endregion #region 类型转换 /// /// 成功时返回值,失败时返回 null(值类型) /// public static T? ToNullable(this Result result) where T : struct => result.IsSuccess ? result.Match(succ: v => (T?)v, fail: _ => null) : null; /// /// 将可空引用类型转换为 Result /// public static Result ToResult( this T? value, string errorMessage = "Value is null") where T : class => value is not null ? Result.Succeed(value) : Result.Fail(new ArgumentNullException(errorMessage)); /// /// 将可空值类型转换为 Result /// public static Result ToResult( this T? value, string errorMessage = "Value is null") where T : struct => value.HasValue ? Result.Succeed(value.Value) : Result.Fail(new ArgumentNullException(errorMessage)); #endregion }