mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
feat(functional): 添加函数式编程支持和Result类型实现
- 实现了Result和Result<T>结构体,提供函数式错误处理 - 添加了AsyncExtensions中的函数式编程命名空间引用 - 在FunctionExtensions中添加函数式编程相关引用 - 从项目文件中移除LanguageExt.Core依赖包 - 重构GlobalUsings.cs移除不必要的全局引用 - 添加ResultExtensions扩展方法支持函数式操作 - 实现Map、Bind、Match等核心函数式编程方法 - 添加异步操作支持和安全执行委托功能
This commit is contained in:
parent
ee3f8c97df
commit
1cb7dfdb14
0
GFramework.Core.Tests/functional/ResultTests.cs
Normal file
0
GFramework.Core.Tests/functional/ResultTests.cs
Normal file
@ -11,7 +11,6 @@
|
||||
<ProjectReference Include="..\$(AssemblyName).Abstractions\$(AssemblyName).Abstractions.csproj"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LanguageExt.Core" Version="4.4.9"/>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.3"/>
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0"/>
|
||||
<PackageReference Include="Arch" Version="2.1.0"/>
|
||||
|
||||
@ -15,7 +15,4 @@ global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Linq;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using LanguageExt.Common;
|
||||
global using LanguageExt.Effects;
|
||||
global using LanguageExt.Pretty;
|
||||
global using System.Threading.Tasks;
|
||||
@ -1,3 +1,5 @@
|
||||
using GFramework.Core.Functional;
|
||||
|
||||
namespace GFramework.Core.extensions;
|
||||
|
||||
/// <summary>
|
||||
|
||||
254
GFramework.Core/extensions/ResultExtensions.cs
Normal file
254
GFramework.Core/extensions/ResultExtensions.cs
Normal file
@ -0,0 +1,254 @@
|
||||
// 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.
|
||||
|
||||
using GFramework.Core.Functional;
|
||||
|
||||
namespace GFramework.Core.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Result 类型的扩展方法,提供函数式编程支持
|
||||
/// </summary>
|
||||
public static class ResultExtensions
|
||||
{
|
||||
#region 聚合
|
||||
|
||||
/// <summary>
|
||||
/// 将多个结果合并,全部成功则返回值列表,遇到第一个失败即短路返回
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var combined = new[]
|
||||
/// {
|
||||
/// Result<int>.Succeed(1),
|
||||
/// Result<int>.Succeed(2),
|
||||
/// Result<int>.Succeed(3)
|
||||
/// }.Combine(); // Result<List<int>> [1, 2, 3]
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static Result<List<T>> Combine<T>(
|
||||
this IEnumerable<Result<T>> results)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(results);
|
||||
|
||||
var values = new List<T>();
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (result.IsFaulted)
|
||||
return Result<List<T>>.Fail(result.Exception);
|
||||
values.Add(result.Match(succ: v => v, fail: _ => default!));
|
||||
}
|
||||
|
||||
return Result<List<T>>.Succeed(values);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 变换
|
||||
|
||||
/// <summary>
|
||||
/// 映射成功值到新类型
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var result = Result<int>.Succeed(42);
|
||||
/// var mapped = result.Map(x => x.ToString()); // Result<string> "42"
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static Result<TResult> Map<T, TResult>(
|
||||
this Result<T> result,
|
||||
Func<T, TResult> mapper)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(mapper);
|
||||
return result.Map(mapper);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绑定操作,将结果链式转换
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// 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")));
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static Result<TResult> Bind<T, TResult>(
|
||||
this Result<T> result,
|
||||
Func<T, Result<TResult>> binder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(binder);
|
||||
return result.Bind(binder);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 副作用
|
||||
|
||||
/// <summary>
|
||||
/// 成功时执行副作用,返回原始结果(可链式调用)
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// Result<int>.Succeed(42)
|
||||
/// .OnSuccess(x => Console.WriteLine($"Value: {x}"))
|
||||
/// .OnFailure(ex => Console.WriteLine($"Error: {ex.Message}"));
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static Result<T> OnSuccess<T>(
|
||||
this Result<T> result,
|
||||
Action<T> action)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(action);
|
||||
result.IfSucc(action);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 失败时执行副作用,返回原始结果(可链式调用)
|
||||
/// </summary>
|
||||
public static Result<T> OnFailure<T>(
|
||||
this Result<T> result,
|
||||
Action<Exception> action)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(action);
|
||||
result.IfFail(action);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 验证
|
||||
|
||||
/// <summary>
|
||||
/// 确保成功值满足条件,否则转换为失败
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var result = Result<int>.Succeed(42)
|
||||
/// .Ensure(x => x > 0, "Value must be positive")
|
||||
/// .Ensure(x => x < 100, "Value must be less than 100");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static Result<T> Ensure<T>(
|
||||
this Result<T> result,
|
||||
Func<T, bool> predicate,
|
||||
string errorMessage)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(predicate);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(errorMessage);
|
||||
|
||||
if (result.IsFaulted) return result;
|
||||
|
||||
return result.Match(
|
||||
succ: value => predicate(value)
|
||||
? result
|
||||
: Result<T>.Fail(new ArgumentException(errorMessage)),
|
||||
fail: _ => result
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure 的异常重载,可以传入更具体的异常类型
|
||||
/// </summary>
|
||||
public static Result<T> Ensure<T>(
|
||||
this Result<T> result,
|
||||
Func<T, bool> predicate,
|
||||
Func<T, Exception> exceptionFactory)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(predicate);
|
||||
ArgumentNullException.ThrowIfNull(exceptionFactory);
|
||||
|
||||
if (result.IsFaulted) return result;
|
||||
|
||||
return result.Match(
|
||||
succ: value => predicate(value)
|
||||
? result
|
||||
: Result<T>.Fail(exceptionFactory(value)),
|
||||
fail: _ => result
|
||||
);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 安全执行
|
||||
|
||||
/// <summary>
|
||||
/// 安全执行同步委托,自动捕获异常为 Result
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var result = ResultExtensions.Try(() => int.Parse("42"));
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static Result<T> Try<T>(Func<T> func)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(func);
|
||||
return Result<T>.Try(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全执行异步委托,自动捕获异常为 Result
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var result = await ResultExtensions.TryAsync(async () => await GetDataAsync());
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static async Task<Result<T>> TryAsync<T>(Func<Task<T>> func)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(func);
|
||||
try
|
||||
{
|
||||
return Result<T>.Succeed(await func());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result<T>.Fail(ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 类型转换
|
||||
|
||||
/// <summary>
|
||||
/// 成功时返回值,失败时返回 null(值类型)
|
||||
/// </summary>
|
||||
public static T? ToNullable<T>(this Result<T> result) where T : struct =>
|
||||
result.IsSuccess
|
||||
? result.Match(succ: v => (T?)v, fail: _ => null)
|
||||
: null;
|
||||
|
||||
/// <summary>
|
||||
/// 将可空引用类型转换为 Result
|
||||
/// </summary>
|
||||
public static Result<T> ToResult<T>(
|
||||
this T? value,
|
||||
string errorMessage = "Value is null") where T : class =>
|
||||
value is not null
|
||||
? Result<T>.Succeed(value)
|
||||
: Result<T>.Fail(new ArgumentNullException(errorMessage));
|
||||
|
||||
/// <summary>
|
||||
/// 将可空值类型转换为 Result
|
||||
/// </summary>
|
||||
public static Result<T> ToResult<T>(
|
||||
this T? value,
|
||||
string errorMessage = "Value is null") where T : struct =>
|
||||
value.HasValue
|
||||
? Result<T>.Succeed(value.Value)
|
||||
: Result<T>.Fail(new ArgumentNullException(errorMessage));
|
||||
|
||||
#endregion
|
||||
}
|
||||
376
GFramework.Core/functional/Result.T.cs
Normal file
376
GFramework.Core/functional/Result.T.cs
Normal file
@ -0,0 +1,376 @@
|
||||
// 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.
|
||||
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace GFramework.Core.Functional;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个操作的结果,可能是成功值或异常
|
||||
/// </summary>
|
||||
public readonly struct Result<A> : IEquatable<Result<A>>, IComparable<Result<A>>
|
||||
{
|
||||
private readonly A? _value;
|
||||
private readonly Exception? _exception;
|
||||
|
||||
// ------------------------------------------------------------------ 状态枚举
|
||||
|
||||
/// <summary>
|
||||
/// 结果状态枚举,表示结果的不同状态
|
||||
/// </summary>
|
||||
private enum ResultState : byte
|
||||
{
|
||||
Bottom,
|
||||
Success,
|
||||
Faulted
|
||||
}
|
||||
|
||||
private readonly ResultState _state;
|
||||
|
||||
// ------------------------------------------------------------------ 静态默认值
|
||||
|
||||
/// <summary>
|
||||
/// 表示未初始化的 Bottom 状态
|
||||
/// </summary>
|
||||
public static readonly Result<A> Bottom = default;
|
||||
|
||||
// ------------------------------------------------------------------ 构造器
|
||||
|
||||
/// <summary>
|
||||
/// 构造成功结果
|
||||
/// </summary>
|
||||
/// <param name="value">成功的值</param>
|
||||
public Result(A value)
|
||||
{
|
||||
_state = ResultState.Success;
|
||||
_value = value;
|
||||
_exception = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造失败结果
|
||||
/// </summary>
|
||||
/// <param name="exception">失败的异常</param>
|
||||
public Result(Exception exception)
|
||||
{
|
||||
_state = ResultState.Faulted;
|
||||
_exception = exception ?? throw new ArgumentNullException(nameof(exception));
|
||||
_value = default;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 隐式转换
|
||||
|
||||
/// <summary>
|
||||
/// 隐式将值转换为成功结果
|
||||
/// </summary>
|
||||
/// <param name="value">要转换的值</param>
|
||||
/// <returns>成功结果</returns>
|
||||
[Pure]
|
||||
public static implicit operator Result<A>(A value) => new(value);
|
||||
|
||||
// ------------------------------------------------------------------ 状态属性
|
||||
|
||||
/// <summary>
|
||||
/// 判断结果是否为成功状态
|
||||
/// </summary>
|
||||
[Pure]
|
||||
public bool IsSuccess => _state == ResultState.Success;
|
||||
|
||||
/// <summary>
|
||||
/// 判断结果是否为失败状态
|
||||
/// </summary>
|
||||
[Pure]
|
||||
public bool IsFaulted => _state == ResultState.Faulted;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 判断结果是否为未初始化的 Bottom 状态
|
||||
/// </summary>
|
||||
[Pure]
|
||||
public bool IsBottom => _state == ResultState.Bottom;
|
||||
|
||||
/// <summary>
|
||||
/// 获取内部异常,若为 Bottom 状态则抛出 InvalidOperationException
|
||||
/// </summary>
|
||||
[Pure]
|
||||
public Exception Exception => _exception
|
||||
?? new InvalidOperationException("Result is in Bottom state.");
|
||||
|
||||
// ------------------------------------------------------------------ 取值
|
||||
|
||||
/// <summary>
|
||||
/// 若成功则返回值,若失败则返回默认值
|
||||
/// </summary>
|
||||
/// <param name="defaultValue">失败时返回的默认值</param>
|
||||
/// <returns>成功时的值或默认值</returns>
|
||||
[Pure]
|
||||
public A IfFail(A defaultValue) => IsSuccess ? _value! : defaultValue;
|
||||
|
||||
/// <summary>
|
||||
/// 若成功则返回值,若失败则通过委托处理异常
|
||||
/// </summary>
|
||||
/// <param name="f">处理异常的委托</param>
|
||||
/// <returns>成功时的值或委托处理后的结果</returns>
|
||||
[Pure]
|
||||
public A IfFail(Func<Exception, A> f) => IsSuccess ? _value! : f(Exception);
|
||||
|
||||
/// <summary>
|
||||
/// 若失败则执行副作用
|
||||
/// </summary>
|
||||
/// <param name="f">处理异常的副作用委托</param>
|
||||
public void IfFail(Action<Exception> f)
|
||||
{
|
||||
if (IsFaulted) f(Exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 若成功则执行副作用
|
||||
/// </summary>
|
||||
/// <param name="f">处理成功值的副作用委托</param>
|
||||
public void IfSucc(Action<A> f)
|
||||
{
|
||||
if (IsSuccess) f(_value!);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 变换
|
||||
|
||||
/// <summary>
|
||||
/// 成功时映射值,失败时透传异常
|
||||
/// </summary>
|
||||
/// <typeparam name="B">映射后的类型</typeparam>
|
||||
/// <param name="f">映射函数</param>
|
||||
/// <returns>映射后的结果</returns>
|
||||
[Pure]
|
||||
public Result<B> Map<B>(Func<A, B> f) =>
|
||||
IsSuccess ? new Result<B>(f(_value!)) : new Result<B>(Exception);
|
||||
|
||||
/// <summary>
|
||||
/// 成功时绑定到新 Result,失败时透传异常
|
||||
/// </summary>
|
||||
/// <typeparam name="B">绑定后的类型</typeparam>
|
||||
/// <param name="binder">绑定函数</param>
|
||||
/// <returns>绑定后的结果</returns>
|
||||
[Pure]
|
||||
public Result<B> Bind<B>(Func<A, Result<B>> binder) =>
|
||||
IsSuccess ? binder(_value!) : new Result<B>(Exception);
|
||||
|
||||
/// <summary>
|
||||
/// 异步映射
|
||||
/// </summary>
|
||||
/// <typeparam name="B">映射后的类型</typeparam>
|
||||
/// <param name="f">异步映射函数</param>
|
||||
/// <returns>异步映射后的结果</returns>
|
||||
[Pure]
|
||||
public async Task<Result<B>> MapAsync<B>(Func<A, Task<B>> f) =>
|
||||
IsSuccess ? new Result<B>(await f(_value!)) : new Result<B>(Exception);
|
||||
|
||||
// ------------------------------------------------------------------ 模式匹配
|
||||
|
||||
/// <summary>
|
||||
/// 对成功/失败两种情况分别处理并返回值
|
||||
/// </summary>
|
||||
/// <typeparam name="R">返回值类型</typeparam>
|
||||
/// <param name="succ">处理成功值的函数</param>
|
||||
/// <param name="fail">处理异常的函数</param>
|
||||
/// <returns>处理后的结果</returns>
|
||||
[Pure]
|
||||
public R Match<R>(Func<A, R> succ, Func<Exception, R> fail) =>
|
||||
IsSuccess ? succ(_value!) : fail(Exception);
|
||||
|
||||
/// <summary>
|
||||
/// 对成功/失败两种情况分别执行副作用
|
||||
/// </summary>
|
||||
/// <param name="succ">处理成功值的副作用委托</param>
|
||||
/// <param name="fail">处理异常的副作用委托</param>
|
||||
public void Match(Action<A> succ, Action<Exception> fail)
|
||||
{
|
||||
if (IsSuccess) succ(_value!);
|
||||
else fail(Exception);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 静态工厂(语义更清晰)
|
||||
|
||||
/// <summary>
|
||||
/// 创建成功结果
|
||||
/// </summary>
|
||||
/// <param name="value">成功的值</param>
|
||||
/// <returns>成功结果</returns>
|
||||
[Pure]
|
||||
public static Result<A> Succeed(A value) => new(value);
|
||||
|
||||
/// <summary>
|
||||
/// 创建成功结果(别名)
|
||||
/// </summary>
|
||||
/// <param name="value">成功的值</param>
|
||||
/// <returns>成功结果</returns>
|
||||
[Pure]
|
||||
public static Result<A> Success(A value) => new(value);
|
||||
|
||||
/// <summary>
|
||||
/// 创建失败结果
|
||||
/// </summary>
|
||||
/// <param name="ex">失败的异常</param>
|
||||
/// <returns>失败结果</returns>
|
||||
[Pure]
|
||||
public static Result<A> Fail(Exception ex) => new(ex);
|
||||
|
||||
/// <summary>
|
||||
/// 创建失败结果(别名)
|
||||
/// </summary>
|
||||
/// <param name="ex">失败的异常</param>
|
||||
/// <returns>失败结果</returns>
|
||||
[Pure]
|
||||
public static Result<A> Failure(Exception ex) => new(ex);
|
||||
|
||||
/// <summary>
|
||||
/// 根据错误消息创建失败结果
|
||||
/// </summary>
|
||||
/// <param name="message">错误消息</param>
|
||||
/// <returns>失败结果</returns>
|
||||
[Pure]
|
||||
public static Result<A> Failure(string message) => new(new Exception(message));
|
||||
|
||||
/// <summary>
|
||||
/// 安全执行委托,自动捕获异常
|
||||
/// </summary>
|
||||
/// <param name="f">要执行的委托</param>
|
||||
/// <returns>执行结果</returns>
|
||||
public static Result<A> Try(Func<A> f)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new Result<A>(f());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new Result<A>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 相等 / 比较
|
||||
|
||||
/// <summary>
|
||||
/// 判断两个结果是否相等
|
||||
/// </summary>
|
||||
/// <param name="other">另一个结果</param>
|
||||
/// <returns>若相等返回 true,否则返回 false</returns>
|
||||
[Pure]
|
||||
public bool Equals(Result<A> other)
|
||||
{
|
||||
if (_state != other._state) return false;
|
||||
if (IsSuccess) return EqualityComparer<A>.Default.Equals(_value, other._value);
|
||||
if (IsFaulted)
|
||||
return Exception.GetType() == other.Exception.GetType()
|
||||
&& Exception.Message == other.Exception.Message;
|
||||
return true; // both Bottom
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断对象是否与当前结果相等
|
||||
/// </summary>
|
||||
/// <param name="obj">要比较的对象</param>
|
||||
/// <returns>若相等返回 true,否则返回 false</returns>
|
||||
[Pure]
|
||||
public override bool Equals(object? obj) => obj is Result<A> other && Equals(other);
|
||||
|
||||
/// <summary>
|
||||
/// 获取结果的哈希码
|
||||
/// </summary>
|
||||
/// <returns>哈希码</returns>
|
||||
[Pure]
|
||||
public override int GetHashCode() => IsSuccess
|
||||
? HashCode.Combine(0, _value)
|
||||
: HashCode.Combine(1, Exception.GetType(), Exception.Message);
|
||||
|
||||
/// <summary>
|
||||
/// 比较两个结果的大小
|
||||
/// </summary>
|
||||
/// <param name="other">另一个结果</param>
|
||||
/// <returns>比较结果</returns>
|
||||
[Pure]
|
||||
public int CompareTo(Result<A> other)
|
||||
{
|
||||
// Bottom < Faulted < Success
|
||||
if (_state != other._state) return _state.CompareTo(other._state);
|
||||
if (!IsSuccess) return 0;
|
||||
return Comparer<A>.Default.Compare(_value, other._value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断两个结果是否相等
|
||||
/// </summary>
|
||||
/// <param name="a">第一个结果</param>
|
||||
/// <param name="b">第二个结果</param>
|
||||
/// <returns>若相等返回 true,否则返回 false</returns>
|
||||
[Pure]
|
||||
public static bool operator ==(Result<A> a, Result<A> b) => a.Equals(b);
|
||||
|
||||
/// <summary>
|
||||
/// 判断两个结果是否不相等
|
||||
/// </summary>
|
||||
/// <param name="a">第一个结果</param>
|
||||
/// <param name="b">第二个结果</param>
|
||||
/// <returns>若不相等返回 true,否则返回 false</returns>
|
||||
[Pure]
|
||||
public static bool operator !=(Result<A> a, Result<A> b) => !a.Equals(b);
|
||||
|
||||
/// <summary>
|
||||
/// 判断第一个结果是否小于第二个结果
|
||||
/// </summary>
|
||||
/// <param name="a">第一个结果</param>
|
||||
/// <param name="b">第二个结果</param>
|
||||
/// <returns>若小于返回 true,否则返回 false</returns>
|
||||
[Pure]
|
||||
public static bool operator <(Result<A> a, Result<A> b) => a.CompareTo(b) < 0;
|
||||
|
||||
/// <summary>
|
||||
/// 判断第一个结果是否小于等于第二个结果
|
||||
/// </summary>
|
||||
/// <param name="a">第一个结果</param>
|
||||
/// <param name="b">第二个结果</param>
|
||||
/// <returns>若小于等于返回 true,否则返回 false</returns>
|
||||
[Pure]
|
||||
public static bool operator <=(Result<A> a, Result<A> b) => a.CompareTo(b) <= 0;
|
||||
|
||||
/// <summary>
|
||||
/// 判断第一个结果是否大于第二个结果
|
||||
/// </summary>
|
||||
/// <param name="a">第一个结果</param>
|
||||
/// <param name="b">第二个结果</param>
|
||||
/// <returns>若大于返回 true,否则返回 false</returns>
|
||||
[Pure]
|
||||
public static bool operator >(Result<A> a, Result<A> b) => a.CompareTo(b) > 0;
|
||||
|
||||
/// <summary>
|
||||
/// 判断第一个结果是否大于等于第二个结果
|
||||
/// </summary>
|
||||
/// <param name="a">第一个结果</param>
|
||||
/// <param name="b">第二个结果</param>
|
||||
/// <returns>若大于等于返回 true,否则返回 false</returns>
|
||||
[Pure]
|
||||
public static bool operator >=(Result<A> a, Result<A> b) => a.CompareTo(b) >= 0;
|
||||
|
||||
// ------------------------------------------------------------------ 调试
|
||||
|
||||
/// <summary>
|
||||
/// 返回结果的字符串表示
|
||||
/// </summary>
|
||||
/// <returns>结果的字符串表示</returns>
|
||||
[Pure]
|
||||
public override string ToString() => _state switch
|
||||
{
|
||||
ResultState.Success => _value?.ToString() ?? "(null)",
|
||||
ResultState.Faulted => $"Fail({Exception.Message})",
|
||||
_ => "(Bottom)"
|
||||
};
|
||||
}
|
||||
160
GFramework.Core/functional/Result.cs
Normal file
160
GFramework.Core/functional/Result.cs
Normal file
@ -0,0 +1,160 @@
|
||||
// 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.
|
||||
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace GFramework.Core.Functional;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个无值的操作结果,仅包含成功或失败状态
|
||||
/// </summary>
|
||||
public readonly struct Result : IEquatable<Result>
|
||||
{
|
||||
private readonly Exception? _exception;
|
||||
private readonly bool _isSuccess;
|
||||
|
||||
/// <summary>
|
||||
/// 私有构造函数,用于创建 Result 实例
|
||||
/// </summary>
|
||||
/// <param name="isSuccess">是否为成功状态</param>
|
||||
/// <param name="exception">失败时的异常信息</param>
|
||||
private Result(bool isSuccess, Exception? exception)
|
||||
{
|
||||
_isSuccess = isSuccess;
|
||||
_exception = exception;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断结果是否为成功状态
|
||||
/// </summary>
|
||||
[Pure]
|
||||
public bool IsSuccess => _isSuccess;
|
||||
|
||||
/// <summary>
|
||||
/// 判断结果是否为失败状态
|
||||
/// </summary>
|
||||
[Pure]
|
||||
public bool IsFailure => !_isSuccess;
|
||||
|
||||
/// <summary>
|
||||
/// 获取失败时的异常信息,若为成功状态则抛出 InvalidOperationException
|
||||
/// </summary>
|
||||
[Pure]
|
||||
public Exception Error => IsFailure
|
||||
? _exception!
|
||||
: throw new InvalidOperationException("Cannot access Error on a successful Result.");
|
||||
|
||||
/// <summary>
|
||||
/// 创建成功结果
|
||||
/// </summary>
|
||||
/// <returns>成功结果</returns>
|
||||
[Pure]
|
||||
public static Result Success() => new(true, null);
|
||||
|
||||
/// <summary>
|
||||
/// 创建失败结果
|
||||
/// </summary>
|
||||
/// <param name="ex">失败的异常</param>
|
||||
/// <returns>失败结果</returns>
|
||||
[Pure]
|
||||
public static Result Failure(Exception ex) => new(false, ex);
|
||||
|
||||
/// <summary>
|
||||
/// 根据错误消息创建失败结果
|
||||
/// </summary>
|
||||
/// <param name="message">错误消息</param>
|
||||
/// <returns>失败结果</returns>
|
||||
[Pure]
|
||||
public static Result Failure(string message) => new(false, new Exception(message));
|
||||
|
||||
/// <summary>
|
||||
/// 根据成功或失败状态分别执行不同的处理逻辑
|
||||
/// </summary>
|
||||
/// <typeparam name="R">返回值类型</typeparam>
|
||||
/// <param name="onSuccess">成功时执行的函数</param>
|
||||
/// <param name="onFailure">失败时执行的函数</param>
|
||||
/// <returns>处理后的结果</returns>
|
||||
public R Match<R>(Func<R> onSuccess, Func<Exception, R> onFailure) =>
|
||||
IsSuccess ? onSuccess() : onFailure(_exception!);
|
||||
|
||||
/// <summary>
|
||||
/// 将当前无值的 Result 提升为带值的 Result
|
||||
/// </summary>
|
||||
/// <typeparam name="A">值的类型</typeparam>
|
||||
/// <param name="value">成功时关联的值</param>
|
||||
/// <returns>带值的 Result</returns>
|
||||
[Pure]
|
||||
public Result<A> ToResult<A>(A value) =>
|
||||
IsSuccess ? Result<A>.Success(value) : Result<A>.Failure(_exception!);
|
||||
|
||||
/// <summary>
|
||||
/// 判断当前 Result 是否与另一个 Result 相等
|
||||
/// </summary>
|
||||
/// <param name="other">另一个 Result</param>
|
||||
/// <returns>若相等返回 true,否则返回 false</returns>
|
||||
[Pure]
|
||||
public bool Equals(Result other)
|
||||
{
|
||||
// 比较状态和异常信息
|
||||
return _isSuccess == other._isSuccess &&
|
||||
(!IsFailure || (_exception?.GetType() == other._exception?.GetType() &&
|
||||
_exception?.Message == other._exception?.Message));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断当前对象是否与另一个对象相等
|
||||
/// </summary>
|
||||
/// <param name="obj">另一个对象</param>
|
||||
/// <returns>若相等返回 true,否则返回 false</returns>
|
||||
[Pure]
|
||||
public override bool Equals(object? obj) => obj is Result other && Equals(other);
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前 Result 的哈希码
|
||||
/// </summary>
|
||||
/// <returns>哈希码</returns>
|
||||
[Pure]
|
||||
public override int GetHashCode()
|
||||
{
|
||||
// 根据状态和异常信息生成哈希码
|
||||
return IsSuccess
|
||||
? HashCode.Combine(true)
|
||||
: HashCode.Combine(false, _exception?.GetType(), _exception?.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断两个 Result 是否相等
|
||||
/// </summary>
|
||||
/// <param name="a">第一个 Result</param>
|
||||
/// <param name="b">第二个 Result</param>
|
||||
/// <returns>若相等返回 true,否则返回 false</returns>
|
||||
[Pure]
|
||||
public static bool operator ==(Result a, Result b) => a.Equals(b);
|
||||
|
||||
/// <summary>
|
||||
/// 判断两个 Result 是否不相等
|
||||
/// </summary>
|
||||
/// <param name="a">第一个 Result</param>
|
||||
/// <param name="b">第二个 Result</param>
|
||||
/// <returns>若不相等返回 true,否则返回 false</returns>
|
||||
[Pure]
|
||||
public static bool operator !=(Result a, Result b) => !a.Equals(b);
|
||||
|
||||
/// <summary>
|
||||
/// 返回当前 Result 的字符串表示
|
||||
/// </summary>
|
||||
/// <returns>Result 的字符串表示</returns>
|
||||
[Pure]
|
||||
public override string ToString() =>
|
||||
IsSuccess ? "Success" : $"Fail({_exception?.Message ?? "Unknown"})";
|
||||
}
|
||||
@ -12,6 +12,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using GFramework.Core.Functional;
|
||||
|
||||
namespace GFramework.Core.functional.functions;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user