可为空的引用类型和"[CS8603] Possible null reference return."



我几乎成功地将我的一个库更新为"可为null"。但是下面的代码给我带来了麻烦。

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返回发出警告,我可以理解这一点,就像我用null初始化result一样。然而,该方法实际上永远不会返回null,result将始终是validator的最新返回值。

pragma warning diable是我唯一的选择,还是我遗漏了什么?我应该补充一点,我不想改变我的方法的返回类型。

编辑:将类Either重命名为Result(返回类型(,因为它分散了对问题的注意力。还包括该类的代码。正如评论中所问的那样,这种类型确实受到了FP的启发,确切地说是来自Kotlin Arrow,但它从来没有打算表现得像它一样。

// Warning produced here
return result! as Result<string, TValid>.Valid;

您使用的是as运算符,该运算符可能返回null,因此生成的类型可以为null。如果您认为类型可能不匹配,并且希望在不匹配的情况下计算为null,请使用as。如果你认为它应该总是匹配的,那么就使用一个常规的演员阵容。

return (Result<string, TValid>.Valid)result!;

如果您认为result必须是Valid类型的假设是错误的,这将抛出,而这正是您想要的。您可能还想添加一个null检查,以确保您不会以某种方式返回null(就像上次validator返回null一样。((强制转换null值是合法的,因此强制转换不会捕获它。(

即使对于函数式程序员来说,这个问题也不清楚,因为它不包含关于"任一"类型的信息。它是一个自定义类吗?它来自特定的库吗?什么是API?

C#没有"非此即彼"类型和F#,F#确实有区别的并集,Result type使用不同的语法。事实上,只有对Haskell略知一二的人可能知道left类型应该是"error"值。

在任何情况下,链接结果的功能方式都是让每个函数接收前一个函数的输入,如果是错误,则立即返回。如果没有,请应用该函数。这通常被称为面向铁路的编程。

如果你有一个函数列表/可枚举/数组(在这种情况下是验证器(,你可以使用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;
}

最新更新