namespace MS.Microservice.Core.Functional { using static F; public static partial class F { public static Validation Valid(T value) => new(value); // 创建表示不正确状态的Validation public static Validation.Invalid Invalid(params Error[] errors) => new(errors); public static Validation Invalid(params Error[] errors) => new Validation.Invalid(errors); public static Validation.Invalid Invalid(IEnumerable errors) => new(errors); public static Validation Invalid(IEnumerable errors) => new Validation.Invalid(errors); } /// /// Validation 的 Valid 容器,包装成功值。 /// public readonly struct Valid { internal T Value { get; } internal Valid(T value) { ArgumentNullException.ThrowIfNull(value, nameof(value)); Value = value; } public override string ToString() => $"Valid({Value})"; } /// /// Either<Error, T> 的特定版本:Invalid 表示失败,Valid 表示成功。 /// public readonly struct Validation : IEquatable> { internal IEnumerable Errors { get; } private readonly T? _value; public bool IsValid { get; } public bool IsInvalid => !IsValid; /// 为 true 时,返回内部包装的有效值。 public T Value => IsValid ? _value! : throw new InvalidOperationException("Validation is Invalid."); /// 的别名,与书中原始 API 保持一致。 public T Valid => Value; /// 为 true 时,返回对应的 结构。 public Validation.Invalid Invalid => new(Errors); private Validation(IEnumerable errors) { IsValid = false; Errors = errors; _value = default; } internal Validation(T right) { IsValid = true; _value = right; Errors = []; } // the Return function for Validation public static Func> Return = t => Valid(t); public static Validation Fail(IEnumerable errors) => new(errors); public static Validation Fail(params Error[] errors) => new(errors.AsEnumerable()); public static implicit operator Validation(Error error) => new([error]); public static implicit operator Validation(Validation.Invalid left) => new(left.Errors); public static implicit operator Validation(T right) => Valid(right); public TR Match(Func, TR> invalid, Func valid) => IsValid ? valid(_value!) : invalid(Errors); public Unit Match(Action> invalid, Action valid) => Match(invalid.ToFunc(), valid.ToFunc()); public Validation Bind(Func> binder) => Match( invalid: error => (Validation)F.Invalid(error), valid: binder); public IEnumerator AsEnumerable() { if (IsValid) yield return _value; } public override string ToString() => IsValid ? $"Valid({Value})" : $"Invalid([{string.Join(", ", Errors)}])"; public bool Equals(Validation other) { if (IsValid != other.IsValid) return false; if (IsValid) return EqualityComparer.Default.Equals(_value, other._value); return Errors.SequenceEqual(other.Errors); } public override bool Equals(object? obj) => obj is Validation other && Equals(other); public override int GetHashCode() { if (IsValid) return HashCode.Combine(true, EqualityComparer.Default.GetHashCode(_value!)); var hc = new HashCode(); hc.Add(false); foreach (var err in Errors) hc.Add(err); return hc.ToHashCode(); } public static bool operator ==(Validation left, Validation right) => left.Equals(right); public static bool operator !=(Validation left, Validation right) => !left.Equals(right); } public static class Validation { public readonly struct Invalid { internal IEnumerable Errors { get; } internal Invalid(IEnumerable errors) => Errors = errors; /// 取第一个 ,便于单错误场景下的断言。 public string Code => Errors.FirstOrDefault()?.Code ?? string.Empty; /// 取第一个 public string Message => Errors.FirstOrDefault()?.Message ?? string.Empty; public override string ToString() => $"Invalid({string.Join(", ", Errors)})"; } extension(IEnumerable>> validators) { /// /// 将多个验证器聚合为一个,所有验证规则都会被执行,所有错误被收集后统一返回。 /// /// 与 Bind 不同(遇到第一个错误即短路), /// 这里使用的是"错误聚合"语义:即使前面的规则已经失败,后续规则仍会执行。 /// /// /// 对应《C# 函数式编程》7.6.2 节 HarvestErrors 函数: /// IEnumerable<Func<T, Validation<T>>> -> Func<T, Validation<T>> /// /// /// /// /// var validateAll = new Func<RegisterAccountCommand, Validation<RegisterAccountCommand>>[] /// { /// ValidateAccount, /// ValidatePassword, /// ValidateEmail /// }.HarvestErrors(); /// /// var result = validateAll(command); /// /// public Func> HarvestErrors() => input => { Validation initial = F.Valid(input); return validators.Aggregate(initial, (accumulated, validator) => { var current = validator(input); // 两个都通过 → 返回最新的 Valid 结果 if (accumulated.IsValid && current.IsValid) return current; // 只有当前验证失败 → 以当前错误作为累积值 if (accumulated.IsValid) return current; // 只有已有累积错误 → 保留累积 if (current.IsValid) return accumulated; // 两个都失败 → 合并所有 Error,形成包含所有错误的单一 Invalid return F.Invalid(accumulated.Invalid.Errors.Concat(current.Invalid.Errors)); }); }; } extension(Validation> valF) { public Validation Apply(Validation valT) => valF.Match( valid: (f) => valT.Match( valid: (t) => F.Valid(f(t)), invalid: (err) => F.Invalid(err) ), invalid: (errF) => valT.Match( valid: (_) => F.Invalid(errF), invalid: (errT) => F.Invalid(errF.Concat(errT)) )); } extension(Validation opt) { public Validation Bind(Func> f) => opt.Match( invalid: (err) => Invalid(err), valid: r => f(r)); public T GetOrThrow() => opt.Match( (errs) => throw new InvalidOperationException($"Validation failed with errors: {string.Join(", ", errs)}"), (t) => t); public T GetOrElse(T defaultValue) => opt.Match( (errs) => defaultValue, (t) => t); public T GetOrElse(Func fallback) => opt.Match( (errs) => fallback(), (t) => t); } public static Validation> Apply (this Validation> @this, Validation arg) => Apply(@this.Map(F.Curry), arg); public static Validation> Apply (this Validation> @this, Validation arg) => Apply(@this.Map(F.CurryFirst), arg); public static Validation> Apply (this Validation> @this, Validation arg) => Apply(@this.Map(F.CurryFirst), arg); public static Validation> Apply (this Validation> @this, Validation arg) => Apply(@this.Map(F.CurryFirst), arg); public static Validation> Apply (this Validation> @this, Validation arg) => Apply(@this.Map(F.CurryFirst), arg); public static Validation> Apply (this Validation> @this, Validation arg) => Apply(@this.Map(F.CurryFirst), arg); public static Validation> Apply (this Validation> @this, Validation arg) => Apply(@this.Map(F.CurryFirst), arg); public static Validation> Apply (this Validation> @this, Validation arg) => Apply(@this.Map(F.CurryFirst), arg); public static Validation Map (this Validation @this, Func f) => @this.IsValid ? Valid(f(@this.Value!)) : Invalid(@this.Errors); public static Validation> Map(this Validation @this , Func func) => @this.Map(func.Curry()); public static Validation ForEach (this Validation @this, Action act) => Map(@this, act.ToFunc()); public static Validation Do (this Validation @this, Action action) { @this.ForEach(action); return @this; } // LINQ public static Validation Select(this Validation @this , Func map) => @this.Map(map); public static Validation SelectMany(this Validation @this , Func> bind, Func project) => @this.Match( invalid: (err) => Invalid(err), valid: (t) => bind(t).Match( invalid: (err) => Invalid(err), valid: (r) => Valid(project(t, r)))); } }