【问题标题】:How to address CS8607 warning about DisallowNull?如何解决有关 DisallowNull 的 CS8607 警告?
【发布时间】:2021-09-07 08:50:37
【问题描述】:

考虑在具有nullable reference types 功能的 .NET 5 类库中的这个 C# 类启用:

public class ContraMapComparer<T, T1> : IEqualityComparer<T1>
{
    private readonly IEqualityComparer<T> source;
    private readonly Func<T1, T> selector;

    public ContraMapComparer(IEqualityComparer<T> source, Func<T1, T> selector)
    {
        this.source = source;
        this.selector = selector;
    }

    public bool Equals(T1? x, T1? y)
    {
        if (x is null && y is null)
            return true;
        if (x is null || y is null)
            return false;

        return source.Equals(selector(x), selector(y));
    }

    public int GetHashCode([DisallowNull] T1 obj)
    {
        return source.GetHashCode(selector(obj));
    }
}

[DisallowNull] 属性由 Visual Studio 的启用接口快速操作添加。

编译时,编译器会发出以下警告:

CS8607    A possible null value may not be used for a type marked with [NotNull] or [DisallowNull]

显然,如果您启用了将警告视为错误(我有),这意味着代码无法编译。

如何解决这个问题?

我尝试将运行时空保护添加到 objsourceselector,但这没有帮助。我还尝试将[DisallowNull] 属性添加到sourceselector,但这也无济于事。

[DisallowNull] 阅读the documentation 并没有让我更加明智。

即使删除[DisallowNull] 属性也无法解决问题。编译器警告仍然存在。

我能够识别的唯一解决方法是使用#pragma 来抑制警告:

#pragma warning disable CS8607 // A possible null value may not be used for a type marked with [NotNull] or [DisallowNull]
                return source.GetHashCode(selector(obj));
#pragma warning restore CS8607 // A possible null value may not be used for a type marked with [NotNull] or [DisallowNull]

还有其他解决办法吗?

【问题讨论】:

  • 您是否尝试过使用!return source.GetHashCode(selector(obj)!); 例如。 (我不清楚表达式的哪一部分导致了警告 - 它可能是 selector(obj) 部分;您可以将其拆分为单独的表达式进行检查。)
  • 如果objnull,你可以——而且可能应该——也抛出。
  • @vernou - 在问题中 - “即使删除 [DisallowNull] 属性也无法解决问题”
  • 你有没有试过给selector(obj)result添加一个空保护?
  • 根据您实际尝试对您的类型执行的操作,添加 where T : notnull 约束也可以解决问题。

标签: c# nullable-reference-types


【解决方案1】:

警告不是在谈论GetHashCode 方法参数上的[DisallowNull] 属性。它正在谈论source.GetHashCode 电话!毕竟这个调用也是调用IEqualityComparer.GetHashCode,它有[DisallowNull]属性。

selector(obj) 返回一个T,但T 对其没有约束,因此它可以是一个可为空的类型,如string?int?。如果它是一个可为空的类型,它可能是空的。但是source.GetHashCode 不允许空值!因此发出警告。

我尝试向objsourceselector 添加运行时空保护

您应该检查selector(obj),而不是sourceselectorobjselector(obj)是可以为null的东西,这里不允许为null。

T t = selector(obj);
if (t == null) {
    throw new ArgumentException("obj maps to a null!", nameof(obj));
}
return source.GetHashCode(t);

【讨论】:

    【解决方案2】:

    Func&lt;T1,T&gt; 未注释为不返回 null。引入一个支持该功能的新委托:

    [return:NotNull]
    public delegate T NonNullFunc<in T1, out T>(T1 value);
    public class ContraMapComparer<T, T1> : IEqualityComparer<T1>
    {
        private readonly IEqualityComparer<T> source;
        private readonly NonNullFunc<T1, T> selector;
    
        public ContraMapComparer(IEqualityComparer<T> source, NonNullFunc<T1, T> selector)
        {
        //As before
    

    这比原谅空值或使用编译指示要好,因为它应该强制使用任何表达式来定义selector,它不会返回null

    (您可能还希望将输入参数注释给委托以禁止空值)

    【讨论】:

    • 这里明显的缺点是一个新的公共类型NonNullFunc。这对ContraMapComparer 来说很好,但无法跨类型扩展,尤其是来自不同作者的类型(想象一下还必须处理NotNullFunc,当然还有来自不同程序集的NonNullFunc)。如果客户习惯于不断地传递Funcs,这对于客户来说也不是一个很好的界面,因为没有隐式转换。
    • 如果我们确实想表达ContraMapComparer 只处理非null Ts,那么简单地将其添加为类型约束可能会更好。如果我们必须保持打开nullTs 的选项,即使是单独的NonNullContraMapComparer 也可能比新的委托更好(因为它不会与任何东西发生冲突),尽管正确共享实现可能会很痛苦。
    【解决方案3】:

    虽然我很欣赏前面两个解释问题所在的答案,但我发现没有一个建议的解决方案是最佳的。

    幸运的是,Jeroen Mostert 在 cmets 中提出了一个更好的解决方案:将 T 约束为 notnull

    public class ContraMapComparer<T, T1> : IEqualityComparer<T1> where T : notnull
    

    这就是我所做的,并且解决了问题。

    【讨论】:

      猜你喜欢
      • 2013-04-29
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 2012-06-14
      • 2017-11-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多