【问题标题】:Object validation with nullable reference types in C# [duplicate]在 C# 中使用可为空引用类型的对象验证 [重复]
【发布时间】:2020-12-07 15:38:13
【问题描述】:

假设我有一个使用 C# 可为空引用对象功能的配置或 DTO 对象:

public class ServiceConfig
    {
        public string ConnectionString { get; set; }
        public string? OptionalComment { get; set; }
    }

使用上面的代码,我收到编译器警告“CS8618:退出构造函数时不可为空的属性 'ConnectionString' 必须包含非空值。考虑将属性声明为可空。”因为这个对象 需要 无参数构造函数,我可以添加 = null! 来消除警告。但问题是对象仍然可以包含具有空值的属性。因为我想在配置和 DTO 的上下文中使用它,所以可以选择在将对象传递给“真实”对象之前验证对象(在反序列化过程中)。

我该怎么做?有一种方法可以通过“可空引用”表示法验证对象是否有效?我看到其他选项也与数据注释有关,但我使用这个 C# 功能更有吸引力。

所以,在幻想中我想要这样的东西:

public class ServiceConfig
{
    public string ConnectionString { get; set; } = null!;
    public string? OptionalComment { get; set; }
}

public class Deserializer
{
    public static ServiceConfig Deserialize(string data)
    {
        var result = Json.Deserialize<ServiceConfig>(data);
        var isObjectValid = result.IsValid(); // I want method something like this
        if (!isObjectValid) throw new Exception("Deserialization error");
        return result;
    }
}

【问题讨论】:

  • 这能回答你的问题吗? How to use .NET reflection to check for nullable reference type。基本上,您将检查每个属性是否可以为空,如果不是,则检查它是否包含null。 NRT 注释的存储方式并非微不足道,因此该检查也不是微不足道的。注释可以实现更多的验证,而不仅仅是可空性。
  • 如果你可以使用 Json.Net 应该可以使用非空构造函数:stackoverflow.com/questions/23017716/…
  • 我的建议是检查是否可以使用源生成器 (devblogs.microsoft.com/dotnet/introducing-c-source-generators),这将为您提供 IsValid() 方法
  • 您是否考虑过添加一个无参数构造函数,将值设置为 "" 而不是 null?
  • 这里的根本问题是你有一个单一的类型试图代表两种不同的状态——一个未验证的对象(ConnectionString 可以为空)和一个验证的对象(不能)。如果您想对事物真正严格,您可能希望将这些概念分成两种类型 - 一种仅用于反序列化目的,然后让您的 Validate 方法实际验证 并将 转换为更严格的类型。这确实意味着很多重复,但根据我的经验,它可以带来各种好处。

标签: c# nullable-reference-types


【解决方案1】:

根据文档@@https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references

编译器不添加任何空检查或其他运行时构造 在可为空的上下文中。在运行时,一个可为空的引用和一个 不可为空的引用是等价的。

因此,当 DTO 在运行时被反序列化时,您可能没有任何额外的构造来表示字段是可空引用类型还是不可空引用类型,至少在撰写此答案时。

【讨论】:

    【解决方案2】:

    似乎不需要显式声明get setter,所以你可以用任何默认值声明public成员,让编译器完美地为你完成它的工作。 :D

    public string ConnectionString = null;
    

    【讨论】:

    • 嗯,现在这是一个与属性有很大不同的字段,我希望这会给出相同的错误,因为您有一个不可为空的字段声明,并且有一个空值初始化...
    • 我认为问题来自反序列化例程,所以唯一的方法是将属性更改为成员。顺便说一句,字符串对象可以为空,默认情况下为空值。
    • 我认为您错过了可空引用类型的要点。属性也是成员-我认为您的意思是字段。但我没有看到反序列化有任何问题的迹象。我相信你误解了问题的重点。
    • 是的,当然。 JSON 反序列化通常确实适用于属性。问题根本不在于反序列化 - 它是不可为空的成员的默认值为 null。
    • 我不会尝试在评论线程中解释可空引用类型功能。这太复杂了。我建议你阅读docs.microsoft.com/en-us/dotnet/csharp/nullable-references
    猜你喜欢
    • 1970-01-01
    • 2016-05-28
    • 2013-11-17
    • 1970-01-01
    • 1970-01-01
    • 2019-04-10
    • 2020-12-13
    • 1970-01-01
    • 2021-08-08
    相关资源
    最近更新 更多