feat(functional): 添加函数式编程支持和Result类型实现

- 实现了Result和Result<T>结构体,提供函数式错误处理
- 添加了AsyncExtensions中的函数式编程命名空间引用
- 在FunctionExtensions中添加函数式编程相关引用
- 从项目文件中移除LanguageExt.Core依赖包
- 重构GlobalUsings.cs移除不必要的全局引用
- 添加ResultExtensions扩展方法支持函数式操作
- 实现Map、Bind、Match等核心函数式编程方法
- 添加异步操作支持和安全执行委托功能
This commit is contained in:
GeWuYou 2026-02-25 15:09:51 +08:00 committed by gewuyou
parent ee3f8c97df
commit 1cb7dfdb14
8 changed files with 794 additions and 5 deletions

View 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"/>

View File

@ -16,6 +16,3 @@ 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;

View File

@ -1,3 +1,5 @@
using GFramework.Core.Functional;
namespace GFramework.Core.extensions;
/// <summary>

View 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&lt;int&gt;.Succeed(1),
/// Result&lt;int&gt;.Succeed(2),
/// Result&lt;int&gt;.Succeed(3)
/// }.Combine(); // Result&lt;List&lt;int&gt;&gt; [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&lt;int&gt;.Succeed(42);
/// var mapped = result.Map(x => x.ToString()); // Result&lt;string&gt; "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&lt;int&gt;.Succeed(42);
/// var bound = result.Bind(x => x > 0
/// ? Result&lt;string&gt;.Succeed(x.ToString())
/// : Result&lt;string&gt;.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&lt;int&gt;.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&lt;int&gt;.Succeed(42)
/// .Ensure(x => x > 0, "Value must be positive")
/// .Ensure(x => x &lt; 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
}

View 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)"
};
}

View 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"})";
}

View File

@ -12,6 +12,7 @@
// limitations under the License.
using System.Collections.Concurrent;
using GFramework.Core.Functional;
namespace GFramework.Core.functional.functions;