【问题标题】:Nullable reference types and "[CS8603] Possible null reference return."可空引用类型和“[CS8603] 可能的空引用返回。”
【发布时间】:2023-04-06 21:20:01
【问题描述】:

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

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 给我一个return 警告,我可以理解,因为我用null 初始化result。然而,该方法永远不会真正返回 null,result 将始终是来自 validator 的最新返回值。

pragma warning diable 是我唯一的选择,还是我错过了什么?我应该补充一点,我不想更改方法 Either 的返回类型(同样,它应该是 null 安全的)。

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

【问题讨论】:

  • edit 将您的完整源代码包含在minimal reproducible example 中,其他人可以编译和测试。还包括您从编译器获得的完整警告,包括行号。
  • 如果您编写类似1.EitherMustBe() 的代码,则返回值将为空,因为您的 foreach 永远不会执行。编译器对此的警告是绝对正确的。
  • @TanveerBadar 正如我的评论所说,这不可能发生。但我包含了完整的代码来显示检查。但是,问题很可能是无法通过该支票提取。
  • 但那是运行时,我不确定编译器有多聪明。它可能仍会得出与我进行检查时相同的结论。
  • @Index 什么是Either?如果它是一个类,那么您提供的Func&lt;TValid, Either&lt;string, TValid&gt;&gt; 可以返回null。请edit您的问题包括您作为 MCVE 拥有的完整源代码。

标签: c# c#-8.0 nullable-reference-types


【解决方案1】:
// Warning produced here
return result! as Result<string, TValid>.Valid;

您正在使用as 运算符,它可能返回null,因此结果类型将为空。当您认为类型可能匹配并且您希望它在不匹配的情况下评估为null 时,请使用as。如果您认为它应该始终匹配,请使用常规转换。

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

如果您认为 result 必须是 Valid 类型的假设是错误的,这将抛出,这正是您想要的。您可能还想添加null 检查以确保您不会以某种方式返回null(例如,如果最后一个validator 返回null。)(强制转换null 值是合法的,所以cast 不会捕捉到它。)

【讨论】:

  • 啊,当然 - 没有考虑使用 'as' 运算符的含义。
【解决方案2】:

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

C# 没有 Either 类型,F# 有区分联合,Result Type 使用不同的语法,事实上,只有稍微了解 Haskell 的人可能知道 left type 应该是“错误”值。

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

如果您有一个函数列表/可枚举/数组(在本例中为验证器),您可以使用 F# 中的 fold 或 C# 中的 LINQ 聚合来调用每个验证器并将之前的结果传递给它。不知道 Either 是什么样子,或者你可以为它构造值,我只能提出一个伪代码示例:

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;
}

【讨论】:

  • 我真的很喜欢在回答几周后无法解释的反对意见
猜你喜欢
  • 1970-01-01
  • 2019-07-02
  • 2019-06-02
  • 2021-12-27
  • 1970-01-01
  • 1970-01-01
  • 2020-04-22
  • 2019-12-17
相关资源
最近更新 更多