【问题标题】:Checking for null?检查是否为空?
【发布时间】:2013-12-09 09:10:50
【问题描述】:

在 Dart 中,我们通过构造函数简化了变量的初始化:

例如

class Foo
{
    Bar _bar;

    Foo(this._bar);
}

乍一看,这似乎很方便。但根据我在 95% 的情况下的经验,您会期望发送到构造函数的内容应该是非空的。

例如在 C# 中我会写:

public class Foo
{
    private Bar bar;

    public Foo(Bar bar)
    {
         if (bar == null)
             throw new ArgumentNullException("bar");

         this.bar = bar;
    }
}

所以我的问题是 Dart 中空参数的最佳实践是什么?鉴于我们有一个基本上不鼓励它的语言功能?

【问题讨论】:

    标签: dart


    【解决方案1】:

    在 Dart 的源代码中,他们抛出 ArgumentError
    大多数时候他们不检查null,而是检查变量类型。

    int codeUnitAt(int index) {
      if (index is !int) throw new ArgumentError(index);
      // ...
    

    来源: dart/sdk/lib/_internal/lib/js_string.dart#L17

    factory JSArray.fixed(int length)  {
      if ((length is !int) || (length < 0)) {
        throw new ArgumentError("Length must be a non-negative integer: $length");
      }
      // ...
    

    来源: dart/sdk/lib/_internal/lib/js_array.dart#L25

    【讨论】:

    • 这很有趣。虽然有点奇怪?长度可以是int以外的任何东西吗?虽然我猜它在类型参数和动态参数之间是一致的。
    • @ronag 可以,在未选中模式下。类型不在那里强制执行。
    • @MarioP:嗯,你确定吗?我的意思是在编译时检查类型。在什么情况下使用类型编译的东西会导致运行时类型不正确?
    • @ronag 类型“错误”只是警告,如果您忽略它,它仍然可以正常编译。不同之处在于,在检查模式下,类型错误会引发异常,而在未检查模式下,它运行良好,直到它遇到答案中所示的检查。或者尝试调用一个不存在的类方法或类似的东西。
    • @ronag - 在 dart 中,您可以将类型声明视为断言语句,即断言(长度为 int)。这些断言在生产模式下被关闭 - 在这种模式下,类型可能完全错误,程序仍将运行。查看此article about optional types 了解更多背景信息。
    【解决方案2】:

    这种初始化方法只是使您免于手动分配参数,检查和其他逻辑仍然需要一个主体。我认为这仍然是一个有用的功能。

    class Foo {
    
      var _bar;
    
      Foo(this._bar) {
        if(this._bar==null) throw new ArgumentError(_bar);
      }
    
    }
    

    【讨论】:

    • 嗯,我没想到。虽然我很难在分配后检查值。在常规函数中执行类似操作会使任何异常保证无效。但我想在构造函数的情况下它会没问题。我觉得有点不直观。
    • 另外,我发现在检查本地 var 值后为无效参数抛出异常更加不直观。
    • @ronag 确实有点少见,但作为一个很懒的人,我还是很喜欢的。对我来说,它按我的意图工作 - 执行 f = new Foo(null); 会导致 f 为 NULL,即使异常被捕获并忽略。
    • 应该抛出 new ArgumentError(_bar);
    【解决方案3】:

    这取决于你喜欢什么。

    最常见的代码和你展示的C#类似:

    if (bar == null) throw new ArgumentError("arg is null");
    

    它提供有用的错误消息,并防止下面的代码做错事(比如随机格式化硬盘为空)。

    我会写:

    Foo(Bar bar) : _bar = bar {
      if (bar == null) throw ArgumentError(...);
    }
    

    因为我发现它比其他替代品更具可读性,但如果你愿意,你甚至可以写:

    Foo(Bar bar) : this.bar = bar ?? throw ArgumentError(...);
    

    使用assert(bar != null) 也可以正常工作。它仅在启用断言时才发现问题,但如果只是为了保护自己(例如,在库中的内部类上)就足够了。对于面向公众的函数和类,我更喜欢 if-throw。

    【讨论】:

    • 也许像Guava Preconditions#checkNotNull 这样的东西可能有用(在quiver 中?)。
    • &lt;!-- language-all: lang-dart --&gt; 应该不再需要语法高亮了。
    • 哦,谢谢你的信息。写起来并不是一件轻而易举的事:)
    • @lm 如果我们有多个参数,那么呢?
    • 单独检查每个参数应该不是问题。我只会抛出发现的第一个问题,而不是尝试收集所有问题,所以它只是更多相同的代码。
    【解决方案4】:

    您是否将“简化初始化”称为“不鼓励空值检查的语言功能”...我可能误解了您的问题。

    无论如何,这是我的方法:

    class About  {
    
      final String title;
      final String text;
    
      const About({    
        customTitle,
        customText,
      }) :
        title = customTitle ?? "",
        text = customText ?? "";
    }
    

    【讨论】:

      【解决方案5】:

      您可以使用 assert 进行检查

      Chapter 2. A Tour of the Dart Language

      assert(text != null);
      

      断言语句仅在检查模式下工作。它们在生产模式下无效。

      所以assert便于开发,但不影响生产性能。

      如果您希望检查在生产环境中持续存在,您可以像在 C# 中一样进行操作

      if (bar == null) {
        throw new ArgumentError('Bar must not be null');
      }
      

      Exceptions - Dart Tips, Ep 9

      【讨论】:

      • 是的,如果担心性能,可以使用断言。使用异常或断言的任何一种方式都与问题无关。
      • 再一次,我知道我可以在 dart 中使用异常。请阅读该问题,特别是“最佳实践”和“基本上不鼓励它的语言功能”。
      • 我必须承认我不明白你的问题。
      • 我认为这与最佳实践无关。并非每个字段都由构造函数参数设置。您可以在字段或构造函数参数上提供默认值,或者在构造函数中计算值并将其分配给字段。
      • 我还没有看到这个特定示例的任何“最佳实践”指南。我要做的是对重要的公共 API 使用运行时空检查和异常。对于其他方法,只需使用断言语句,或者什么都不用。我不认为你能想出一刀切的规则。我见过 cmets 建议在运行时检查类型通常是错误的做法。 IE。 if (foo is!String) throw new Boom();
      猜你喜欢
      • 2010-12-23
      • 2013-07-18
      • 2013-07-12
      • 2013-07-27
      • 2015-08-09
      • 2017-06-14
      • 2017-10-07
      • 2019-09-30
      • 1970-01-01
      相关资源
      最近更新 更多