为什么 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 目前,显然。
以下是一些相关资源:
Here's the LDM that lead to the creation of [MemberNotNull]
in the first place.
Here's the PR from February 2020 that added support for [MemberNotNull]
to Roslyn, the C# compiler.
Here's a (currently open) GitHub issue about the reverse scenario:其中 derived-type 不能使用 [MemberNotNull]
对从超类型继承的成员进行断言。
Here's an earlier dupe of that issue that was actually closed as by design,除了 Roslyn team-member 声明:
@jaredpar 2021-07-07: The C# language design team decided that MemberNotNull
could only apply to declared members on the type where the attribute was used.
所以即使他指的是在 derived-type 上使用 [MemberNotNull]
来指代超类型成员,它也排除了 [MemberNotNull]
在超类型中使用来指代可能被覆盖 子类成员。
请记住,被覆盖的成员不会 与其超类型中相应的 abstract
或 virtual
成员共享身份(而继承非虚拟成员共享身份),因此(就分析器而言)[MemberNotNull( "Member" )]can only refer to
Base.Memberand not
Derived.Member`.
当然可以说它应该得到支持,我怀疑这是决定使用与[=]相同的成员查找代码的结果92=] DefaultMemberAttribute
.
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.
为什么要使用字符串?
为什么要在要验证的class里面写validation?
您确定派生的 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);
消息 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 目前,显然。
以下是一些相关资源:
Here's the LDM that lead to the creation of
[MemberNotNull]
in the first place.Here's the PR from February 2020 that added support for
[MemberNotNull]
to Roslyn, the C# compiler.Here's a (currently open) GitHub issue about the reverse scenario:其中 derived-type 不能使用
[MemberNotNull]
对从超类型继承的成员进行断言。Here's an earlier dupe of that issue that was actually closed as by design,除了 Roslyn team-member 声明:
@jaredpar 2021-07-07: The C# language design team decided that
MemberNotNull
could only apply to declared members on the type where the attribute was used.所以即使他指的是在 derived-type 上使用
[MemberNotNull]
来指代超类型成员,它也排除了[MemberNotNull]
在超类型中使用来指代可能被覆盖 子类成员。请记住,被覆盖的成员不会 与其超类型中相应的
abstract
或virtual
成员共享身份(而继承非虚拟成员共享身份),因此(就分析器而言)[MemberNotNull( "Member" )]can only refer to
Base.Memberand not
Derived.Member`.
当然可以说它应该得到支持,我怀疑这是决定使用与[=]相同的成员查找代码的结果92=]
DefaultMemberAttribute
.
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.
为什么要使用字符串?
为什么要在要验证的class里面写validation?
您确定派生的 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);