A:初版提交
This commit is contained in:
1 parent
c9778a3e7c
commit
43ff9bc57f
21 files changed
+1655
No files matched your search
@@ -0,0 +1,7 @@
|
||||
namespace Common
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Common.ExpandException;
|
||||
|
||||
public class ExampleExceptionEnum
|
||||
{
|
||||
// region 系统常见错误本地化
|
||||
|
||||
/// <summary>
|
||||
/// 进程信息获取失败
|
||||
/// </summary>
|
||||
public static readonly ExceptionExpand ProcessGetException = new(100001L, "进程消息获取失败,指定线程可能不存在或已关闭!", "以管理员权限重试!");
|
||||
|
||||
// endregion
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace Common.ExpandException;
|
||||
|
||||
public class ExceptionExpand : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认异常编码
|
||||
/// </summary>
|
||||
private const long DefaultCode = -1;
|
||||
|
||||
public string ErrorMessage { get; private set; } = "";
|
||||
public long ErrorCode { get; private set; } = DefaultCode;
|
||||
public string Suggestion { get; private set; } = "";
|
||||
|
||||
private ExceptionExpand()
|
||||
{
|
||||
}
|
||||
|
||||
public ExceptionExpand(long errorCode, string message, string solution) : base(
|
||||
message)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
Suggestion = solution;
|
||||
}
|
||||
|
||||
public ExceptionExpand(long errorCode, string message, string solution, Exception? innerException) : base(
|
||||
message, innerException)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
Suggestion = solution;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Error Code: {ErrorCode}\nMessage: {Message}\nSuggestion: {Suggestion}\n{base.ToString()}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
namespace Common.ExpandFun;
|
||||
|
||||
public static class StringExpand
|
||||
{
|
||||
/// <summary>
|
||||
/// 检测是否是 url
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsValidUrl(this string str)
|
||||
{
|
||||
return Uri.TryCreate(str, UriKind.Absolute, out _);
|
||||
}
|
||||
|
||||
public static int ToInt(this string? str, int defaultValue = default)
|
||||
{
|
||||
return TryToInt(str) ?? defaultValue;
|
||||
}
|
||||
|
||||
public static int? TryToInt(this string? str)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(str))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return int.Parse(str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static double ToDouble(this string? str, double defaultValue = default)
|
||||
{
|
||||
return TryToDouble(str) ?? defaultValue;
|
||||
}
|
||||
|
||||
public static double? TryToDouble(this string? str)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(str))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return double.Parse(str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Common;
|
||||
|
||||
/// <summary>
|
||||
/// 服务生命周期
|
||||
/// </summary>
|
||||
public enum ServiceLifetime
|
||||
{
|
||||
/// <summary>
|
||||
/// 单例:整个应用程序生命周期内只创建一个实例
|
||||
/// </summary>
|
||||
Singleton,
|
||||
|
||||
/// <summary>
|
||||
/// 瞬态:每次请求都创建新实例
|
||||
/// </summary>
|
||||
Transient
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务变更事件参数
|
||||
/// </summary>
|
||||
public class ServiceChangedEventArgs<T> : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// 旧值
|
||||
/// </summary>
|
||||
public T? OldValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 新值
|
||||
/// </summary>
|
||||
public T NewValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 变更时间
|
||||
/// </summary>
|
||||
public DateTime ChangedAt { get; }
|
||||
|
||||
public ServiceChangedEventArgs(T? oldValue, T newValue)
|
||||
{
|
||||
OldValue = oldValue;
|
||||
NewValue = newValue;
|
||||
ChangedAt = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轻量级依赖注入容器(线程安全)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 支持功能:
|
||||
/// - 线程安全的服务注册和获取
|
||||
/// - 生命周期管理(Singleton/Transient)
|
||||
/// - 服务变更通知机制
|
||||
/// - 支持工厂方法注册
|
||||
/// </remarks>
|
||||
public static class Global
|
||||
{
|
||||
private class ServiceDescriptor
|
||||
{
|
||||
public Type ServiceType { get; }
|
||||
public ServiceLifetime Lifetime { get; }
|
||||
public Func<object> Factory { get; }
|
||||
public object? SingletonInstance { get; set; }
|
||||
public readonly object Lock = new();
|
||||
|
||||
public ServiceDescriptor(Type serviceType, Func<object> factory, ServiceLifetime lifetime)
|
||||
{
|
||||
ServiceType = serviceType;
|
||||
Factory = factory;
|
||||
Lifetime = lifetime;
|
||||
}
|
||||
}
|
||||
|
||||
// 服务描述符存储(线程安全)
|
||||
private static readonly ConcurrentDictionary<Type, ServiceDescriptor> _services = new();
|
||||
|
||||
// 变更通知订阅者存储(线程安全)
|
||||
private static readonly ConcurrentDictionary<Type, List<Delegate>> _subscribers = new();
|
||||
|
||||
// 订阅者列表的锁
|
||||
private static readonly ConcurrentDictionary<Type, object> _subscriberLocks = new();
|
||||
|
||||
#region 注册服务
|
||||
|
||||
/// <summary>
|
||||
/// 注册单例服务实例
|
||||
/// </summary>
|
||||
/// <typeparam name="T">服务类型</typeparam>
|
||||
/// <param name="instance">服务实例</param>
|
||||
/// <param name="notifyIfExists">如果已存在是否触发变更通知(默认false)</param>
|
||||
public static void RegisterSingleton<T>(T instance, bool notifyIfExists = false) where T : class
|
||||
{
|
||||
if (instance == null)
|
||||
throw new ArgumentNullException(nameof(instance));
|
||||
|
||||
var descriptor = new ServiceDescriptor(
|
||||
typeof(T),
|
||||
() => instance,
|
||||
ServiceLifetime.Singleton)
|
||||
{
|
||||
SingletonInstance = instance
|
||||
};
|
||||
|
||||
T? oldValue = null;
|
||||
var isUpdate = false;
|
||||
|
||||
// 如果已存在,保存旧值用于通知
|
||||
if (notifyIfExists && _services.TryGetValue(typeof(T), out var existingDescriptor))
|
||||
{
|
||||
oldValue = existingDescriptor.SingletonInstance as T;
|
||||
isUpdate = true;
|
||||
}
|
||||
|
||||
_services[typeof(T)] = descriptor;
|
||||
|
||||
// 如果是更新且需要通知,触发变更事件
|
||||
if (isUpdate && notifyIfExists)
|
||||
{
|
||||
NotifyChanged(oldValue, instance);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册单例服务(使用工厂方法)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">服务类型</typeparam>
|
||||
/// <param name="factory">创建服务实例的工厂方法</param>
|
||||
public static void RegisterSingleton<T>(Func<T> factory) where T : class
|
||||
{
|
||||
if (factory == null)
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
|
||||
var descriptor = new ServiceDescriptor(
|
||||
typeof(T),
|
||||
() => factory()!,
|
||||
ServiceLifetime.Singleton);
|
||||
|
||||
_services[typeof(T)] = descriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册瞬态服务(每次获取都创建新实例)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">服务类型</typeparam>
|
||||
/// <param name="factory">创建服务实例的工厂方法</param>
|
||||
public static void RegisterTransient<T>(Func<T> factory) where T : class
|
||||
{
|
||||
if (factory == null)
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
|
||||
var descriptor = new ServiceDescriptor(
|
||||
typeof(T),
|
||||
() => factory()!,
|
||||
ServiceLifetime.Transient);
|
||||
|
||||
_services[typeof(T)] = descriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册服务(指定生命周期)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">服务类型</typeparam>
|
||||
/// <param name="factory">创建服务实例的工厂方法</param>
|
||||
/// <param name="lifetime">服务生命周期</param>
|
||||
public static void Register<T>(Func<T> factory, ServiceLifetime lifetime) where T : class
|
||||
{
|
||||
switch (lifetime)
|
||||
{
|
||||
case ServiceLifetime.Singleton:
|
||||
RegisterSingleton(factory);
|
||||
break;
|
||||
case ServiceLifetime.Transient:
|
||||
RegisterTransient(factory);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Unknown lifetime: {lifetime}", nameof(lifetime));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 获取服务
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务实例
|
||||
/// </summary>
|
||||
/// <typeparam name="T">服务类型</typeparam>
|
||||
/// <returns>服务实例</returns>
|
||||
/// <exception cref="InvalidOperationException">服务未注册</exception>
|
||||
public static T Get<T>() where T : class
|
||||
{
|
||||
if (!_services.TryGetValue(typeof(T), out var descriptor))
|
||||
throw new InvalidOperationException($"No service registered for type {typeof(T).Name}");
|
||||
|
||||
return (T)ResolveService(descriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取服务实例
|
||||
/// </summary>
|
||||
/// <typeparam name="T">服务类型</typeparam>
|
||||
/// <param name="instance">服务实例(如果获取失败则为 null)</param>
|
||||
/// <returns>是否成功获取</returns>
|
||||
public static bool TryGet<T>(out T? instance) where T : class
|
||||
{
|
||||
if (_services.TryGetValue(typeof(T), out var descriptor))
|
||||
{
|
||||
instance = (T)ResolveService(descriptor);
|
||||
return true;
|
||||
}
|
||||
|
||||
instance = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务实例或返回默认值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">服务类型</typeparam>
|
||||
/// <param name="defaultValue">默认值</param>
|
||||
/// <returns>服务实例或默认值</returns>
|
||||
public static T? GetOrDefault<T>(T? defaultValue = null) where T : class
|
||||
{
|
||||
return TryGet<T>(out var instance) ? instance : defaultValue;
|
||||
}
|
||||
|
||||
private static object ResolveService(ServiceDescriptor descriptor)
|
||||
{
|
||||
if (descriptor.Lifetime == ServiceLifetime.Singleton)
|
||||
{
|
||||
// 双重检查锁定模式,保证单例只创建一次
|
||||
if (descriptor.SingletonInstance == null)
|
||||
{
|
||||
lock (descriptor.Lock)
|
||||
{
|
||||
if (descriptor.SingletonInstance == null)
|
||||
{
|
||||
descriptor.SingletonInstance = descriptor.Factory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return descriptor.SingletonInstance;
|
||||
}
|
||||
|
||||
// Transient: 每次都创建新实例
|
||||
return descriptor.Factory();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 更新服务
|
||||
|
||||
/// <summary>
|
||||
/// 更新单例服务实例(会触发变更通知)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">服务类型</typeparam>
|
||||
/// <param name="newInstance">新的服务实例</param>
|
||||
/// <exception cref="InvalidOperationException">服务未注册或不是单例</exception>
|
||||
public static void Update<T>(T newInstance) where T : class
|
||||
{
|
||||
if (newInstance == null)
|
||||
throw new ArgumentNullException(nameof(newInstance));
|
||||
|
||||
if (!_services.TryGetValue(typeof(T), out var descriptor))
|
||||
throw new InvalidOperationException($"No service registered for type {typeof(T).Name}");
|
||||
|
||||
if (descriptor.Lifetime != ServiceLifetime.Singleton)
|
||||
throw new InvalidOperationException($"Cannot update non-singleton service of type {typeof(T).Name}");
|
||||
|
||||
T? oldValue;
|
||||
lock (descriptor.Lock)
|
||||
{
|
||||
oldValue = descriptor.SingletonInstance as T;
|
||||
descriptor.SingletonInstance = newInstance;
|
||||
}
|
||||
|
||||
// 触发变更通知
|
||||
NotifyChanged(oldValue, newInstance);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 变更通知
|
||||
|
||||
/// <summary>
|
||||
/// 订阅服务变更通知
|
||||
/// </summary>
|
||||
/// <typeparam name="T">服务类型</typeparam>
|
||||
/// <param name="handler">变更处理程序</param>
|
||||
public static void Subscribe<T>(Action<ServiceChangedEventArgs<T>> handler) where T : class
|
||||
{
|
||||
if (handler == null)
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
var type = typeof(T);
|
||||
var lockObj = _subscriberLocks.GetOrAdd(type, _ => new object());
|
||||
|
||||
lock (lockObj)
|
||||
{
|
||||
var subscribers = _subscribers.GetOrAdd(type, _ => new List<Delegate>());
|
||||
subscribers.Add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消订阅服务变更通知
|
||||
/// </summary>
|
||||
/// <typeparam name="T">服务类型</typeparam>
|
||||
/// <param name="handler">变更处理程序</param>
|
||||
public static void Unsubscribe<T>(Action<ServiceChangedEventArgs<T>> handler) where T : class
|
||||
{
|
||||
if (handler == null)
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
var type = typeof(T);
|
||||
if (!_subscriberLocks.TryGetValue(type, out var lockObj))
|
||||
return;
|
||||
|
||||
lock (lockObj)
|
||||
{
|
||||
if (_subscribers.TryGetValue(type, out var subscribers))
|
||||
{
|
||||
subscribers.Remove(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void NotifyChanged<T>(T? oldValue, T newValue) where T : class
|
||||
{
|
||||
var type = typeof(T);
|
||||
if (!_subscribers.TryGetValue(type, out var subscribers))
|
||||
return;
|
||||
|
||||
var eventArgs = new ServiceChangedEventArgs<T>(oldValue, newValue);
|
||||
|
||||
// 获取订阅者列表的快照,避免在通知过程中锁定
|
||||
List<Delegate> subscribersCopy;
|
||||
if (_subscriberLocks.TryGetValue(type, out var lockObj))
|
||||
{
|
||||
lock (lockObj)
|
||||
{
|
||||
subscribersCopy = new List<Delegate>(subscribers);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
subscribersCopy = new List<Delegate>(subscribers);
|
||||
}
|
||||
|
||||
// 通知所有订阅者
|
||||
foreach (var subscriber in subscribersCopy)
|
||||
{
|
||||
try
|
||||
{
|
||||
(subscriber as Action<ServiceChangedEventArgs<T>>)?.Invoke(eventArgs);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略订阅者抛出的异常,避免影响其他订阅者
|
||||
// 在实际应用中,可以记录日志
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 管理方法
|
||||
|
||||
/// <summary>
|
||||
/// 检查服务是否已注册
|
||||
/// </summary>
|
||||
/// <typeparam name="T">服务类型</typeparam>
|
||||
/// <returns>是否已注册</returns>
|
||||
public static bool IsRegistered<T>() where T : class
|
||||
{
|
||||
return _services.ContainsKey(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注销服务
|
||||
/// </summary>
|
||||
/// <typeparam name="T">服务类型</typeparam>
|
||||
/// <returns>是否成功注销</returns>
|
||||
public static bool Unregister<T>() where T : class
|
||||
{
|
||||
var type = typeof(T);
|
||||
var removed = _services.TryRemove(type, out _);
|
||||
|
||||
// 同时移除相关的订阅者
|
||||
if (removed)
|
||||
{
|
||||
_subscribers.TryRemove(type, out _);
|
||||
_subscriberLocks.TryRemove(type, out _);
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有已注册的服务
|
||||
/// </summary>
|
||||
public static void Clear()
|
||||
{
|
||||
_services.Clear();
|
||||
_subscribers.Clear();
|
||||
_subscriberLocks.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取已注册的服务数量
|
||||
/// </summary>
|
||||
public static int Count => _services.Count;
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
using Common.ExpandException;
|
||||
|
||||
namespace Common.Results;
|
||||
|
||||
public abstract class Result
|
||||
{
|
||||
public bool IsSuccess { get; }
|
||||
public bool IsFailure => !IsSuccess;
|
||||
public Exception? Exception { get; }
|
||||
public string? ErrorMessage { get; }
|
||||
public long? ErrorCode { get; }
|
||||
|
||||
protected Result(bool isSuccess, Exception? exception = null, string? errorMessage = null, long? errorCode = null)
|
||||
{
|
||||
if (isSuccess && exception is not null)
|
||||
throw new ArgumentException("Success result cannot have an exception", nameof(exception));
|
||||
|
||||
if (isSuccess && errorCode.HasValue)
|
||||
throw new ArgumentException("Success result cannot have an error code", nameof(errorCode));
|
||||
|
||||
if (!isSuccess && exception is null && string.IsNullOrEmpty(errorMessage) && !errorCode.HasValue)
|
||||
throw new ArgumentException("Failed result must have either an exception, error message, or error code");
|
||||
|
||||
IsSuccess = isSuccess;
|
||||
Exception = exception;
|
||||
ErrorMessage = errorMessage ?? exception?.Message;
|
||||
ErrorCode = errorCode;
|
||||
|
||||
// If we have an ExceptionExpand, extract the error code from it
|
||||
if (exception is ExceptionExpand exceptionExpand && !errorCode.HasValue)
|
||||
{
|
||||
ErrorCode = exceptionExpand.ErrorCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static Result Success() => new SimpleResult(true);
|
||||
|
||||
public static Result<T> Success<T>(T value) => new Result<T>(value);
|
||||
|
||||
public static Result Failure(string errorMessage) => new SimpleResult(false, null, errorMessage);
|
||||
|
||||
public static Result Failure(Exception exception) => new SimpleResult(false, exception);
|
||||
|
||||
public static Result Failure(long errorCode, string errorMessage) => new SimpleResult(false, null, errorMessage, errorCode);
|
||||
|
||||
public static Result Failure(long errorCode, string errorMessage, Exception exception) => new SimpleResult(false, exception, errorMessage, errorCode);
|
||||
|
||||
public static Result<T> Failure<T>(string errorMessage) => new Result<T>(default, false, null, errorMessage);
|
||||
|
||||
public static Result<T> Failure<T>(Exception exception) => new Result<T>(default, false, exception);
|
||||
|
||||
public static Result<T> Failure<T>(long errorCode, string errorMessage) => new Result<T>(default, false, null, errorMessage, errorCode);
|
||||
|
||||
public static Result<T> Failure<T>(long errorCode, string errorMessage, Exception exception) => new Result<T>(default, false, exception, errorMessage, errorCode);
|
||||
|
||||
public static Result Try(Action action)
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
return Success();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Failure(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static Result<T> Try<T>(Func<T> func)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = func();
|
||||
return Success(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Failure<T>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<Result> TryAsync(Func<Task> taskFunc)
|
||||
{
|
||||
try
|
||||
{
|
||||
await taskFunc();
|
||||
return Success();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Failure(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<Result<T>> TryAsync<T>(Func<Task<T>> taskFunc)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await taskFunc();
|
||||
return Success(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Failure<T>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Result OnSuccess(Action action)
|
||||
{
|
||||
if (IsSuccess)
|
||||
action();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Result OnFailure(Action<Exception?, string?, long?> action)
|
||||
{
|
||||
if (IsFailure)
|
||||
action(Exception, ErrorMessage, ErrorCode);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Result OnFailure(Action<Exception?, string?> action)
|
||||
{
|
||||
if (IsFailure)
|
||||
action(Exception, ErrorMessage);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Result<T> Match<T>(Func<T> onSuccess, Func<Exception?, string?, long?, T> onFailure)
|
||||
{
|
||||
return IsSuccess ? Result.Success(onSuccess()) : Result.Failure<T>(Exception ?? new Exception(ErrorMessage));
|
||||
}
|
||||
|
||||
public Result<T> Match<T>(Func<T> onSuccess, Func<Exception?, string?, T> onFailure)
|
||||
{
|
||||
return IsSuccess ? Result.Success(onSuccess()) : Result.Failure<T>(Exception ?? new Exception(ErrorMessage));
|
||||
}
|
||||
|
||||
private class SimpleResult : Result
|
||||
{
|
||||
public SimpleResult(bool isSuccess, Exception? exception = null, string? errorMessage = null, long? errorCode = null)
|
||||
: base(isSuccess, exception, errorMessage, errorCode)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Result<T> : Result
|
||||
{
|
||||
public T? Value { get; }
|
||||
|
||||
public Result(T? value, bool isSuccess = true, Exception? exception = null, string? errorMessage = null, long? errorCode = null)
|
||||
: base(isSuccess, exception, errorMessage, errorCode)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator Result<T>(T value) => new Result<T>(value);
|
||||
|
||||
public Result<TNew> Map<TNew>(Func<T, TNew> mapper)
|
||||
{
|
||||
if (IsFailure)
|
||||
{
|
||||
if (ErrorCode.HasValue)
|
||||
return Result.Failure<TNew>(ErrorCode.Value, ErrorMessage ?? "Mapping failed", Exception);
|
||||
return Result.Failure<TNew>(Exception ?? new Exception(ErrorMessage));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var mappedValue = mapper(Value!);
|
||||
return Result.Success(mappedValue);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure<TNew>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Result<TNew> Bind<TNew>(Func<T, Result<TNew>> binder)
|
||||
{
|
||||
if (IsFailure)
|
||||
{
|
||||
if (ErrorCode.HasValue)
|
||||
return Result.Failure<TNew>(ErrorCode.Value, ErrorMessage ?? "Binding failed", Exception);
|
||||
return Result.Failure<TNew>(Exception ?? new Exception(ErrorMessage));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return binder(Value!);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure<TNew>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Result OnSuccess(Action<T> action)
|
||||
{
|
||||
if (IsSuccess)
|
||||
action(Value!);
|
||||
return this;
|
||||
}
|
||||
|
||||
public new Result<T> OnFailure(Action<Exception?, string?, long?> action)
|
||||
{
|
||||
if (IsFailure)
|
||||
action(Exception, ErrorMessage, ErrorCode);
|
||||
return this;
|
||||
}
|
||||
|
||||
public new Result<T> OnFailure(Action<Exception?, string?> action)
|
||||
{
|
||||
if (IsFailure)
|
||||
action(Exception, ErrorMessage);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TResult Match<TResult>(Func<T, TResult> onSuccess, Func<Exception?, string?, long?, TResult> onFailure)
|
||||
{
|
||||
return IsSuccess ? onSuccess(Value!) : onFailure(Exception, ErrorMessage, ErrorCode);
|
||||
}
|
||||
|
||||
public TResult Match<TResult>(Func<T, TResult> onSuccess, Func<Exception?, string?, TResult> onFailure)
|
||||
{
|
||||
return IsSuccess ? onSuccess(Value!) : onFailure(Exception, ErrorMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
using Common.ExpandException;
|
||||
|
||||
namespace Common.Results;
|
||||
|
||||
public static class ResultExtensions
|
||||
{
|
||||
public static T? GetOrNull<T>(this Result<T> result) where T : class
|
||||
{
|
||||
return result.IsSuccess ? result.Value : null;
|
||||
}
|
||||
|
||||
public static T? GetOrNullable<T>(this Result<T> result) where T : struct
|
||||
{
|
||||
return result.IsSuccess ? result.Value : null;
|
||||
}
|
||||
|
||||
public static T GetOrDefault<T>(this Result<T> result, T defaultValue = default!)
|
||||
{
|
||||
return result.IsSuccess ? result.Value! : defaultValue;
|
||||
}
|
||||
|
||||
public static T GetOrThrow<T>(this Result<T> result)
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
return result.Value!;
|
||||
|
||||
if (result.Exception is not null)
|
||||
throw result.Exception;
|
||||
|
||||
throw new InvalidOperationException(result.ErrorMessage ?? "Operation failed");
|
||||
}
|
||||
|
||||
public static Exception? ExceptionOrNull(this Result result)
|
||||
{
|
||||
return result.IsFailure ? result.Exception : null;
|
||||
}
|
||||
|
||||
public static string? ErrorMessageOrNull(this Result result)
|
||||
{
|
||||
return result.IsFailure ? result.ErrorMessage : null;
|
||||
}
|
||||
|
||||
public static long? ErrorCodeOrNull(this Result result)
|
||||
{
|
||||
return result.IsFailure ? result.ErrorCode : null;
|
||||
}
|
||||
|
||||
public static T GetOrElse<T>(this Result<T> result, Func<T> alternativeProvider)
|
||||
{
|
||||
return result.IsSuccess ? result.Value! : alternativeProvider();
|
||||
}
|
||||
|
||||
public static T GetOrElse<T>(this Result<T> result, Func<Exception?, string?, T> alternativeProvider)
|
||||
{
|
||||
return result.IsSuccess ? result.Value! : alternativeProvider(result.Exception, result.ErrorMessage);
|
||||
}
|
||||
|
||||
public static T GetOrElse<T>(this Result<T> result, Func<Exception?, string?, long?, T> alternativeProvider)
|
||||
{
|
||||
return result.IsSuccess ? result.Value! : alternativeProvider(result.Exception, result.ErrorMessage, result.ErrorCode);
|
||||
}
|
||||
|
||||
public static Result<T> Where<T>(this Result<T> result, Func<T, bool> predicate, string errorMessage = "Predicate not satisfied")
|
||||
{
|
||||
if (result.IsFailure)
|
||||
return result;
|
||||
|
||||
return predicate(result.Value!) ? result : Result.Failure<T>(errorMessage);
|
||||
}
|
||||
|
||||
public static Result<T> Where<T>(this Result<T> result, Func<T, bool> predicate, long errorCode, string errorMessage = "Predicate not satisfied")
|
||||
{
|
||||
if (result.IsFailure)
|
||||
return result;
|
||||
|
||||
return predicate(result.Value!) ? result : Result.Failure<T>(errorCode, errorMessage);
|
||||
}
|
||||
|
||||
public static Result<T> Where<T>(this Result<T> result, Func<T, bool> predicate, Func<T, Exception> exceptionProvider)
|
||||
{
|
||||
if (result.IsFailure)
|
||||
return result;
|
||||
|
||||
return predicate(result.Value!) ? result : Result.Failure<T>(exceptionProvider(result.Value!));
|
||||
}
|
||||
|
||||
public static Result<U> SelectMany<T, U>(this Result<T> result, Func<T, Result<U>> selector)
|
||||
{
|
||||
return result.Bind(selector);
|
||||
}
|
||||
|
||||
public static Result<V> SelectMany<T, U, V>(this Result<T> result, Func<T, Result<U>> selector, Func<T, U, V> resultSelector)
|
||||
{
|
||||
return result.Bind(t => selector(t).Map(u => resultSelector(t, u)));
|
||||
}
|
||||
|
||||
public static Result<U> Select<T, U>(this Result<T> result, Func<T, U> selector)
|
||||
{
|
||||
return result.Map(selector);
|
||||
}
|
||||
|
||||
public static Result<T> DoOnSuccess<T>(this Result<T> result, Action<T> action)
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
action(result.Value!);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result<T> DoOnFailure<T>(this Result<T> result, Action<Exception?, string?> action)
|
||||
{
|
||||
if (result.IsFailure)
|
||||
action(result.Exception, result.ErrorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result<T> DoOnFailure<T>(this Result<T> result, Action<Exception?, string?, long?> action)
|
||||
{
|
||||
if (result.IsFailure)
|
||||
action(result.Exception, result.ErrorMessage, result.ErrorCode);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result<T> DoOnBoth<T>(this Result<T> result, Action<T> onSuccess, Action<Exception?, string?> onFailure)
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
onSuccess(result.Value!);
|
||||
else
|
||||
onFailure(result.Exception, result.ErrorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result<T> DoOnBoth<T>(this Result<T> result, Action<T> onSuccess, Action<Exception?, string?, long?> onFailure)
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
onSuccess(result.Value!);
|
||||
else
|
||||
onFailure(result.Exception, result.ErrorMessage, result.ErrorCode);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result Ignore<T>(this Result<T> result)
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
return Result.Success();
|
||||
|
||||
if (result.ErrorCode.HasValue)
|
||||
return Result.Failure(result.ErrorCode.Value, result.ErrorMessage ?? "Operation failed", result.Exception);
|
||||
|
||||
return Result.Failure(result.Exception ?? new Exception(result.ErrorMessage));
|
||||
}
|
||||
|
||||
public static Result<T> AsResult<T>(this T value) where T : notnull
|
||||
{
|
||||
return Result.Success(value);
|
||||
}
|
||||
|
||||
public static Result<T> AsResultOrFailure<T>(this T? value, string errorMessage = "Value is null") where T : class
|
||||
{
|
||||
return value is not null ? Result.Success(value) : Result.Failure<T>(errorMessage);
|
||||
}
|
||||
|
||||
public static Result<T> AsResultOrFailure<T>(this T? value, string errorMessage = "Value is null") where T : struct
|
||||
{
|
||||
return value.HasValue ? Result.Success(value.Value) : Result.Failure<T>(errorMessage);
|
||||
}
|
||||
|
||||
public static async Task<Result<T>> DoOnSuccessAsync<T>(this Task<Result<T>> resultTask, Func<T, Task> asyncAction)
|
||||
{
|
||||
var result = await resultTask;
|
||||
if (result.IsSuccess)
|
||||
await asyncAction(result.Value!);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<Result<T>> DoOnFailureAsync<T>(this Task<Result<T>> resultTask, Func<Exception?, string?, Task> asyncAction)
|
||||
{
|
||||
var result = await resultTask;
|
||||
if (result.IsFailure)
|
||||
await asyncAction(result.Exception, result.ErrorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<Result<U>> MapAsync<T, U>(this Task<Result<T>> resultTask, Func<T, Task<U>> asyncMapper)
|
||||
{
|
||||
var result = await resultTask;
|
||||
if (result.IsFailure)
|
||||
return Result.Failure<U>(result.Exception ?? new Exception(result.ErrorMessage));
|
||||
|
||||
try
|
||||
{
|
||||
var mappedValue = await asyncMapper(result.Value!);
|
||||
return Result.Success(mappedValue);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure<U>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<Result<U>> BindAsync<T, U>(this Task<Result<T>> resultTask, Func<T, Task<Result<U>>> asyncBinder)
|
||||
{
|
||||
var result = await resultTask;
|
||||
if (result.IsFailure)
|
||||
return Result.Failure<U>(result.Exception ?? new Exception(result.ErrorMessage));
|
||||
|
||||
try
|
||||
{
|
||||
return await asyncBinder(result.Value!);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure<U>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static Result<T> Ensure<T>(this Result<T> result, Func<T, bool> predicate, string errorMessage)
|
||||
{
|
||||
if (result.IsFailure)
|
||||
return result;
|
||||
|
||||
return predicate(result.Value!) ? result : Result.Failure<T>(errorMessage);
|
||||
}
|
||||
|
||||
public static Result<T> Ensure<T>(this Result<T> result, Func<T, bool> predicate, long errorCode, string errorMessage)
|
||||
{
|
||||
if (result.IsFailure)
|
||||
return result;
|
||||
|
||||
return predicate(result.Value!) ? result : Result.Failure<T>(errorCode, errorMessage);
|
||||
}
|
||||
|
||||
public static Result<T> Ensure<T>(this Result<T> result, Func<T, bool> predicate, Func<T, Exception> exceptionFactory)
|
||||
{
|
||||
if (result.IsFailure)
|
||||
return result;
|
||||
|
||||
return predicate(result.Value!) ? result : Result.Failure<T>(exceptionFactory(result.Value!));
|
||||
}
|
||||
|
||||
public static Result<T> OrElse<T>(this Result<T> result, Func<Result<T>> alternativeProvider)
|
||||
{
|
||||
return result.IsSuccess ? result : alternativeProvider();
|
||||
}
|
||||
|
||||
public static Result<T> OrElse<T>(this Result<T> result, Func<Exception?, string?, Result<T>> alternativeProvider)
|
||||
{
|
||||
return result.IsSuccess ? result : alternativeProvider(result.Exception, result.ErrorMessage);
|
||||
}
|
||||
|
||||
public static Result<T> OrElse<T>(this Result<T> result, Func<Exception?, string?, long?, Result<T>> alternativeProvider)
|
||||
{
|
||||
return result.IsSuccess ? result : alternativeProvider(result.Exception, result.ErrorMessage, result.ErrorCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the result has the specified error code
|
||||
/// </summary>
|
||||
public static bool HasErrorCode(this Result result, long errorCode)
|
||||
{
|
||||
return result.IsFailure && result.ErrorCode == errorCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error code or returns the default value
|
||||
/// </summary>
|
||||
public static long GetErrorCodeOrDefault(this Result result, long defaultValue = -1)
|
||||
{
|
||||
return result.ErrorCode ?? defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Result failure with an error code
|
||||
/// </summary>
|
||||
public static Result<T> FailWithCode<T>(long errorCode, string errorMessage)
|
||||
{
|
||||
return Result.Failure<T>(errorCode, errorMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an ExceptionExpand to a Result failure
|
||||
/// </summary>
|
||||
public static Result<T> FromExceptionExpand<T>(ExceptionExpand exception)
|
||||
{
|
||||
return Result.Failure<T>(exception.ErrorCode, exception.Message, exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an ExceptionExpand to a non-generic Result failure
|
||||
/// </summary>
|
||||
public static Result FromExceptionExpand(ExceptionExpand exception)
|
||||
{
|
||||
return Result.Failure(exception.ErrorCode, exception.Message, exception);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
using Common.ExpandException;
|
||||
|
||||
namespace Common.Results;
|
||||
|
||||
/// <summary>
|
||||
/// Examples demonstrating how to use the Result《T》 class and its extensions
|
||||
/// </summary>
|
||||
public static class ResultUsageExamples
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic usage examples
|
||||
/// </summary>
|
||||
public static void BasicUsageExamples()
|
||||
{
|
||||
// Creating successful results
|
||||
var successResult = Result.Success(42);
|
||||
var voidSuccessResult = Result.Success();
|
||||
|
||||
// Creating failed results
|
||||
var failureResult = Result.Failure<int>("Something went wrong");
|
||||
var exceptionResult = Result.Failure<int>(new ArgumentException("Invalid argument"));
|
||||
|
||||
// Creating failed results with error codes
|
||||
var errorCodeResult = Result.Failure<int>(404L, "Not found");
|
||||
var errorCodeWithExceptionResult = Result.Failure<int>(500L, "Internal server error", new Exception("Database connection failed"));
|
||||
|
||||
// Using Try patterns for safe execution
|
||||
var tryResult = Result.Try(() => int.Parse("123"));
|
||||
var tryFailResult = Result.Try(() => int.Parse("invalid"));
|
||||
|
||||
// Async Try patterns
|
||||
var asyncResult = Result.TryAsync(async () => await SomeAsyncOperation());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods examples
|
||||
/// </summary>
|
||||
public static void ExtensionMethodsExamples()
|
||||
{
|
||||
var result = Result.Success(42);
|
||||
|
||||
// Safe value extraction
|
||||
var nullableValue = result.GetOrNullable(); // Returns 42 for value types
|
||||
var defaultValue = result.GetOrDefault(0); // Returns 42
|
||||
var throwValue = result.GetOrThrow(); // Returns 42
|
||||
|
||||
// Alternative providers
|
||||
var alternative = result.GetOrElse(() => 0);
|
||||
var altResult = result.OrElse(() => Result.Success(0));
|
||||
|
||||
// Conversion methods
|
||||
string? nullValue = null;
|
||||
var nullResult = nullValue.AsResultOrFailure(); // Creates failure result
|
||||
var valueResult = "test".AsResult(); // Creates success result
|
||||
|
||||
// Working with failures
|
||||
var failResult = Result.Failure<int>("error");
|
||||
var failNullable = failResult.GetOrNullable(); // Returns null
|
||||
var failDefault = failResult.GetOrDefault(999); // Returns 999
|
||||
var exception = failResult.ExceptionOrNull(); // Returns null (no exception, just message)
|
||||
var errorMsg = failResult.ErrorMessageOrNull(); // Returns "error"
|
||||
|
||||
// Working with error codes
|
||||
var errorCodeResult = Result.Failure<int>(404L, "Not found");
|
||||
var errorCode = errorCodeResult.ErrorCodeOrNull(); // Returns 404
|
||||
var hasSpecificCode = errorCodeResult.HasErrorCode(404L); // Returns true
|
||||
var codeOrDefault = errorCodeResult.GetErrorCodeOrDefault(); // Returns 404
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Functional programming examples
|
||||
/// </summary>
|
||||
public static void FunctionalExamples()
|
||||
{
|
||||
var result = Result.Success(10);
|
||||
|
||||
// Map: Transform successful values
|
||||
var doubled = result.Map(x => x * 2); // Result<int> with value 20
|
||||
|
||||
// Bind: Chain operations that return Results
|
||||
var chained = result
|
||||
.Bind(x => x > 5 ? Result.Success(x * 2) : Result.Failure<int>("Too small"))
|
||||
.Bind(x => Result.Success(x.ToString()));
|
||||
|
||||
// Filter with Where/Ensure
|
||||
var filtered = result
|
||||
.Where(x => x > 5, "Value must be greater than 5")
|
||||
.Ensure(x => x < 100, "Value must be less than 100");
|
||||
|
||||
// Filter with error codes
|
||||
var filteredWithCode = result
|
||||
.Where(x => x > 5, 400L, "Value must be greater than 5")
|
||||
.Ensure(x => x < 100, 422L, "Value must be less than 100");
|
||||
|
||||
// Conditional execution
|
||||
result.OnSuccess(value => Console.WriteLine($"Value: {value}"));
|
||||
result.OnFailure((ex, msg) => Console.WriteLine($"Error: {msg}"));
|
||||
result.OnFailure((ex, msg, code) => Console.WriteLine($"Error {code}: {msg}"));
|
||||
|
||||
// Pattern matching
|
||||
var message = result.Match(
|
||||
onSuccess: value => $"Success: {value}",
|
||||
onFailure: (ex, msg) => $"Error: {msg}");
|
||||
|
||||
var messageWithCode = result.Match(
|
||||
onSuccess: value => $"Success: {value}",
|
||||
onFailure: (ex, msg, code) => $"Error {code}: {msg}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LINQ query syntax examples
|
||||
/// </summary>
|
||||
public static void LinqExamples()
|
||||
{
|
||||
// Using LINQ query syntax with Results
|
||||
var result1 = Result.Success(5);
|
||||
var result2 = Result.Success(10);
|
||||
|
||||
var combined = from x in result1
|
||||
from y in result2
|
||||
where x > 0 && y > 0
|
||||
select x + y;
|
||||
|
||||
// More complex LINQ operations
|
||||
var processed = from value in Result.Success("hello")
|
||||
let upper = value.ToUpper()
|
||||
where upper.Length > 3
|
||||
select $"Processed: {upper}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Async operations examples
|
||||
/// </summary>
|
||||
public static async Task AsyncExamples()
|
||||
{
|
||||
// Async Try patterns
|
||||
var asyncResult = await Result.TryAsync(async () =>
|
||||
{
|
||||
await Task.Delay(100);
|
||||
return 42;
|
||||
});
|
||||
|
||||
// Working with async results
|
||||
if (asyncResult.IsSuccess)
|
||||
{
|
||||
await Task.Delay(50);
|
||||
Console.WriteLine($"Processing: {asyncResult.Value}");
|
||||
}
|
||||
|
||||
// Async mapping and binding
|
||||
var mappedResult = await Task.FromResult(Result.Success(10))
|
||||
.MapAsync(async value =>
|
||||
{
|
||||
await Task.Delay(50);
|
||||
return value * 2;
|
||||
});
|
||||
|
||||
var boundResult = await Task.FromResult(Result.Success(5))
|
||||
.BindAsync(async value =>
|
||||
{
|
||||
await Task.Delay(50);
|
||||
return value > 0 ? Result.Success(value * 2) : Result.Failure<int>("Invalid");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error handling patterns
|
||||
/// </summary>
|
||||
public static void ErrorHandlingExamples()
|
||||
{
|
||||
// Multiple validation steps
|
||||
var input = "123";
|
||||
var validationResult = input.AsResult()
|
||||
.Ensure(s => !string.IsNullOrEmpty(s), "Input cannot be empty")
|
||||
.Bind(s => Result.Try(() => int.Parse(s)))
|
||||
.Ensure(i => i > 0, "Number must be positive")
|
||||
.Map(i => i * 2);
|
||||
|
||||
// Handling different error scenarios
|
||||
validationResult
|
||||
.OnSuccess(value => Console.WriteLine($"Valid result: {value}"))
|
||||
.OnFailure((ex, msg, code) =>
|
||||
{
|
||||
if (ex is FormatException)
|
||||
Console.WriteLine("Invalid number format");
|
||||
else if (code.HasValue)
|
||||
Console.WriteLine($"Validation error {code}: {msg}");
|
||||
else
|
||||
Console.WriteLine($"Validation error: {msg}");
|
||||
});
|
||||
|
||||
// Providing fallback values
|
||||
var safeResult = validationResult.OrElse(() => Result.Success(0));
|
||||
var finalValue = validationResult.GetOrElse(() => 0);
|
||||
|
||||
// Error code specific handling
|
||||
var safeWithCodeHandling = validationResult.OrElse((ex, msg, code) =>
|
||||
{
|
||||
return code switch
|
||||
{
|
||||
400L => Result.Success(0), // Bad request -> default
|
||||
422L => Result.Success(-1), // Validation -> sentinel
|
||||
_ => Result.Success(999) // Unknown -> special value
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Integration with existing code examples
|
||||
/// </summary>
|
||||
public static void IntegrationExamples()
|
||||
{
|
||||
// Converting existing exception-throwing code
|
||||
var legacyResult = Result.Try(() => RiskyOperation());
|
||||
|
||||
// Working with nullable types
|
||||
string? nullableString = GetNullableString();
|
||||
var nullableResult = nullableString.AsResultOrFailure("String was null");
|
||||
|
||||
// Converting Result back to exceptions when needed
|
||||
try
|
||||
{
|
||||
var value = legacyResult.GetOrThrow();
|
||||
// Use value...
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Handle exception...
|
||||
}
|
||||
|
||||
// Ignoring typed results when only success/failure matters
|
||||
var voidResult = Result.Success(42).Ignore();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error code specific examples
|
||||
/// </summary>
|
||||
public static void ErrorCodeExamples()
|
||||
{
|
||||
// Creating failures with error codes
|
||||
var notFoundResult = Result.Failure<string>(404L, "Resource not found");
|
||||
var validationResult = Result.Failure<int>(422L, "Invalid input");
|
||||
var serverError = Result.Failure<object>(500L, "Internal server error", new Exception("Database connection failed"));
|
||||
|
||||
// Using ExceptionExpand with error codes
|
||||
var customException = new ExceptionExpand(1001L, "Custom business error", "Check your input parameters");
|
||||
var fromException = ResultExtensions.FromExceptionExpand<string>(customException);
|
||||
|
||||
// Error code checking and handling
|
||||
if (notFoundResult.HasErrorCode(404L))
|
||||
{
|
||||
Console.WriteLine("Handle not found error");
|
||||
}
|
||||
|
||||
// Switch on error codes
|
||||
var errorCode = validationResult.GetErrorCodeOrDefault();
|
||||
var response = errorCode switch
|
||||
{
|
||||
404L => "Resource not found",
|
||||
422L => "Validation failed",
|
||||
500L => "Server error",
|
||||
_ => "Unknown error"
|
||||
};
|
||||
|
||||
// Error code in functional chains
|
||||
var processedResult = Result.Success("user@example.com")
|
||||
.Ensure(email => email.Contains("@"), 400L, "Invalid email format")
|
||||
.Bind(email => ValidateEmailDomain(email))
|
||||
.Map(email => $"Welcome, {email}");
|
||||
|
||||
// Handling specific error codes
|
||||
processedResult.OnFailure((ex, msg, code) =>
|
||||
{
|
||||
var logLevel = code switch
|
||||
{
|
||||
400L => "WARN", // Validation errors
|
||||
404L => "INFO", // Not found
|
||||
500L => "ERROR", // Server errors
|
||||
_ => "DEBUG"
|
||||
};
|
||||
Console.WriteLine($"[{logLevel}] Error {code}: {msg}");
|
||||
});
|
||||
|
||||
// Creating error code specific results
|
||||
var unauthorizedResult = ResultExtensions.FailWithCode<string>(401L, "Unauthorized access");
|
||||
var forbiddenResult = Result.Failure<string>(403L, "Insufficient permissions");
|
||||
|
||||
// Combining results with error code preservation
|
||||
var combinedResult = from user in GetUserById(123)
|
||||
from permissions in GetUserPermissions(user)
|
||||
where permissions.CanRead
|
||||
select user;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HTTP status code style error handling
|
||||
/// </summary>
|
||||
public static void HttpStatusCodeStyleExamples()
|
||||
{
|
||||
// Common HTTP status codes as error codes
|
||||
var badRequest = Result.Failure<string>(400L, "Bad Request");
|
||||
var unauthorized = Result.Failure<string>(401L, "Unauthorized");
|
||||
var forbidden = Result.Failure<string>(403L, "Forbidden");
|
||||
var notFound = Result.Failure<string>(404L, "Not Found");
|
||||
var conflict = Result.Failure<string>(409L, "Conflict");
|
||||
var unprocessable = Result.Failure<string>(422L, "Unprocessable Entity");
|
||||
var internalError = Result.Failure<string>(500L, "Internal Server Error");
|
||||
|
||||
// Business logic error codes
|
||||
var businessError = Result.Failure<string>(1001L, "Insufficient balance");
|
||||
var domainError = Result.Failure<string>(2001L, "Invalid user state");
|
||||
var integrationError = Result.Failure<string>(3001L, "External service unavailable");
|
||||
|
||||
// Error code ranges for different error types
|
||||
var validationError = Result.Failure<string>(4001L, "Required field missing"); // 4xxx: Validation
|
||||
var businessLogicError = Result.Failure<string>(5001L, "Business rule violation"); // 5xxx: Business
|
||||
var systemError = Result.Failure<string>(6001L, "Database connection failed"); // 6xxx: System
|
||||
}
|
||||
|
||||
// Helper methods for examples
|
||||
private static int RiskyOperation() => throw new InvalidOperationException("Something went wrong");
|
||||
private static string? GetNullableString() => null;
|
||||
private static async Task<string> SomeAsyncOperation()
|
||||
{
|
||||
await Task.Delay(100);
|
||||
return "async result";
|
||||
}
|
||||
|
||||
private static Result<string> ValidateEmailDomain(string email)
|
||||
{
|
||||
var domain = email.Split('@').LastOrDefault();
|
||||
return domain switch
|
||||
{
|
||||
"blocked.com" => Result.Failure<string>(403L, "Domain is blocked"),
|
||||
null => Result.Failure<string>(400L, "Invalid email format"),
|
||||
_ => Result.Success(email)
|
||||
};
|
||||
}
|
||||
|
||||
private static Result<User> GetUserById(int id)
|
||||
{
|
||||
return id switch
|
||||
{
|
||||
123 => Result.Success(new User { Id = 123, Name = "John" }),
|
||||
_ => Result.Failure<User>(404L, "User not found")
|
||||
};
|
||||
}
|
||||
|
||||
private static Result<UserPermissions> GetUserPermissions(User user)
|
||||
{
|
||||
return Result.Success(new UserPermissions { CanRead = true, CanWrite = false });
|
||||
}
|
||||
|
||||
private class User
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
}
|
||||
|
||||
private class UserPermissions
|
||||
{
|
||||
public bool CanRead { get; set; }
|
||||
public bool CanWrite { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Common.Utils;
|
||||
|
||||
public static class JsonHelper
|
||||
{
|
||||
public static readonly JsonSerializerOptions CamelCaseOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
WriteIndented = true,
|
||||
Converters =
|
||||
{
|
||||
new IpAddressConverter(), new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, allowIntegerValues: false)
|
||||
},
|
||||
};
|
||||
|
||||
public static string ToJson<T>(T value) =>
|
||||
JsonSerializer.Serialize(value, CamelCaseOptions);
|
||||
|
||||
public static T? FromJson<T>(string json) =>
|
||||
JsonSerializer.Deserialize<T>(json, CamelCaseOptions);
|
||||
}
|
||||
|
||||
/*
|
||||
IPAddress.ScopeId 属性在某些情况下(如 IPv4 类型)是不被支持的,会抛出异常。
|
||||
这是 .NET 的设计问题,访问 ScopeId 只有在 IPv6 地址中才有意义,在 IPv4 中访问它就会抛出这个 10045 错误。
|
||||
*/
|
||||
public class IpAddressConverter : JsonConverter<IPAddress>
|
||||
{
|
||||
public override IPAddress Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var ipString = reader.GetString() ?? String.Empty;
|
||||
return IPAddress.Parse(ipString);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, IPAddress value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value?.ToString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Common.Utils;
|
||||
|
||||
public static class SystemHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取可用端口
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static int GetAvailablePort()
|
||||
{
|
||||
// todo: 2025/6/24 15:53 开发过程中使用固定端口
|
||||
// return 8267;
|
||||
|
||||
TcpListener listener = new TcpListener(IPAddress.Loopback, 0); // 端口号为0表示由系统自动分配
|
||||
listener.Start();
|
||||
int port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||
listener.Stop();
|
||||
return port;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查当前进程是否以管理员权限运行
|
||||
/// </summary>
|
||||
/// <returns>如果是管理员权限返回 true,否则返回 false</returns>
|
||||
/// <exception cref="PlatformNotSupportedException">仅支持 Windows 平台</exception>
|
||||
/// <remarks>
|
||||
/// 此方法仅适用于 Windows 操作系统。
|
||||
/// 在非 Windows 平台上会抛出 PlatformNotSupportedException 异常。
|
||||
/// </remarks>
|
||||
public static bool IsAdministrator()
|
||||
{
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
throw new PlatformNotSupportedException("IsAdministrator method is only supported on Windows platform.");
|
||||
}
|
||||
|
||||
var identity = System.Security.Principal.WindowsIdentity.GetCurrent();
|
||||
var principal = new System.Security.Principal.WindowsPrincipal(identity);
|
||||
return principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user