为什么 MemberNotNull 不适用于属性覆盖?

Why does MemberNotNull not work for override of properties?

消息 warning CS8604: Possible null reference argument 是在调用 Process() 时发出的。一种修复方法是用 MemberNotNull.

标记它

如果 Derived class 中没有覆盖,则 MemberNotNull 会按预期工作。

如果存在覆盖,为什么编译器仍会发出警告?

什么是抑制此警告的更好方法?

using System;
using System.Diagnostics.CodeAnalysis;

namespace Tests
{
    class Base
    {
        public virtual string? Member { get; set; }

        [MemberNotNull(nameof(Member))]
        public void Validate()
        {
            if (Member is null)
            {
                throw new Exception("Prop is null");
            }
        }
    }

    class Derived : Base
    {
        public override string? Member { get; set; } // warning CS8604: Possible null reference argument
    }

    [TestClass]
    public class MemberNotNullTests
    {
        static void Process(string s)
        {
            Console.WriteLine(s);
        }

        [TestMethod]
        public void TestMemberNotNull()
        {
            Derived instance = new();
            instance.Validate();
            Process(instance.Member);
        }
    }
}```

(这个答案是non-authoritative)

据我所知,这种行为很可能 by-design 目前,显然。

以下是一些相关资源:

This is indirectly mentioned in the LDM notes,但他们没有解释 为什么 或他们是如何做出该决定的,但我认为是因为这会节省时间并导致可预测的行为(或者在这种情况下... 意外 行为):

We propose that the lookup rules for resolving the names in these instances would be similar to the rules for the DefaultMemberAttribute.

奇怪的是,在 PR code-review 注释 中讨论了反向 场景,其中 MemberNotNull 在子类中用于断言 non-overridden继承的成员是 not-null - 但我找不到提到它的明显逆转,这就是我们所关心的:

@jcouv 2022-02-25: Note that a type can only mark its own members as not-null, but not its base's.

  1. 为什么要使用字符串?

  2. 为什么要在要验证的class里面写validation?

  3. 您确定派生的 class 应该覆盖 属性 吗?

我可以推荐:

class Base
    {
      public virtual string Property {get;set;}
    }
    class Derived: Base
    {
      public override string Property {get;set;}
    }
    // can be as service
    public interface IValidator<T> where T: Base
    {
      public bool IsValid(T item);
    }
    
    class Validator : IValidator<T>
    {
      public bool IsValid(T obj) => string.IsNullOrEmpty(obj.Property);
    }

测试中:

        Derived instance = new();
        Validator validator = new();
        validator.Validate(instance);
        Process(instance.Member);