我几乎成功地将我的一个库更新为“可空的”。但是下面的代码给我带来麻烦。
public static Result<string, TValid> ResultMustBe<TValid>
(
this TValid self,
params Func<TValid, Result<string, TValid>>[] validators
)
{
var caller = new StackFrame(1)?.GetMethod()?.DeclaringType?.FullName ?? "?";
if (!validators.Any())
{
throw new ArgumentException($"No validators provided for EitherMustBe in {caller}");
}
Result<string, TValid>? result = null;
foreach (var validator in validators)
{
try
{
result = validator(self);
if (result is Result<string, TValid>.Invalid left)
{
return Result<string, TValid>.MakeInvalid(left.Error.Replace("<<caller>>", caller));
}
}
catch (Exception ex)
{
throw new ValidationException($"Uncaught exception occured while validating {caller}", ex);
}
}
// Warning produced here
return result! as Result<string, TValid>.Valid;
}
public abstract class Result<TInvalid, TValid>
{
public static Valid MakeValid(TValid data)
{
return new Valid(data);
}
public static Invalid MakeInvalid(TInvalid error)
{
return new Invalid(error);
}
public abstract bool IsValid { get; }
public class Invalid : Result<TInvalid, TValid>
{
public TInvalid Error { get; }
public override bool IsValid => false;
public Invalid(TInvalid error)
{
Error = error;
}
}
public class Valid : Result<TInvalid, TValid>
{
public TValid Data { get; }
public override bool IsValid => true;
public Valid(TValid data)
{
Data = data;
}
}
}
编译器向我发出[CS8603] Possible null reference return
的返回警告,我可以用result
初始化null
来理解。但是,该方法永远不会实际返回null,result
始终是validator
的最新返回值。
pragma warning diable
是我唯一的选择,还是我错过了什么?我应该补充一点,就是我不想更改方法的返回类型(同样,它应该是null安全的)。
编辑:由于分散了对问题的注意力,将类Either
重命名为Result
(返回类型)。还包括该类的代码。正如评论中所问的那样,该类型确实是受FP启发的,确切地说是来自Kotlin Arrow的FP,但从未打算完全像它那样表现。
答案 0 :(得分:0)
// Warning produced here
return result! as Result<string, TValid>.Valid;
您正在使用as
运算符,该运算符可能返回null
,因此结果类型将可以为空。如果您认为类型可能不匹配且希望在不匹配的情况下求值为as
,请使用null
。如果您认为它应该始终匹配,请使用常规强制转换。
return (Result<string, TValid>.Valid)result!;
如果您认为result
必须为Valid
类型的假设是错误的,这就是您想要的。您可能还想添加一个null
检查,以确保您不会以某种方式返回null
(就像最后一个validator
返回了null
一样。)(强制转换null
的值,因此强制类型转换不会捕获该值。)
答案 1 :(得分:-1)
这个问题甚至对于函数式程序员来说也不清楚,因为它不包含有关Either类型的信息。它是一个自定义类,它来自特定的库吗?什么是API?
C#没有Either类型,而F#没有区别联合,而Result Type使用不同的语法,实际上,只有对Haskell有所了解的人才知道 left < / em>类型应该是“错误”值。
无论如何,链接结果的功能方式是使每个函数都接收前一个输入,如果是错误,则立即返回。如果不是,则应用该功能。这通常称为Railway oriented programming。
如果您具有函数的列表/可枚举/数组(在本例中为验证器),则可以在F#中使用fold
或在C#中使用LINQ的Aggregate来调用每个验证器并将先前的结果传递给它。不知道是什么样子,或者您可以为其构造值,我只能提出一个伪代码示例:
Either<string,TValue> ValidateEitherMustBe<TValid>
(
this TValid self,
params Func<TValid, Either<string, TValid>>[] validators
)
{
var seed=Either<string, TValid>.MakeValid(self);
var result=validators.Aggregate((previous,validator) =>
previous switch { Either<string, TValid>.Invalid _ => previous,
Either<string, TValid>.Valid value => valid(value),
});
return result;
}