有没有一种速记方法来返回可能为空的值?

Posted

技术标签:

【中文标题】有没有一种速记方法来返回可能为空的值?【英文标题】:Is there a shorthand way to return values that might be null? 【发布时间】:2016-11-11 00:51:36 【问题描述】:

我怎样才能写出以下场景的简写?

get

    if (_rows == null)
    
        _rows = new List<Row>();
    

    return _rows;

【问题讨论】:

您的代码原样正常。它可以缩短,但以可读性为代价。在我看来,节省 3 行是不值得的。 我并不喜欢这种模式。你有一个产生状态变化的吸气剂 @BradThomas 在某些模式下还不错。问题中的示例看起来有点像惰性求值:get 不会改变对象的外部状态。只要不从其他地方访问_rows,那就是... @KABoissonneault 而且只要这个对象不会被多个线程同时读取 @Tavian。问题比这更隐蔽。对于 getter 的使用者来说,能够假设对象在读取属性之前和之后处于相同状态通常是有好处的。否则可能会出现令人惊讶的副作用,违反最小惊讶原则。 【参考方案1】:

使用null-coalescing operator (??):

get 
 
     _rows = _rows ?? new List<Row>(); 
     return _rows; 

OR(可读性较差):

get  return _rows ?? (_rows = new List<Row>()); 

??运算符称为空合并运算符。它返回 如果操作数不为空,则为左操作数;否则返回 右手操作数。

【讨论】:

返回_rows ?? (_rows = new List()); @hvd 为 ?? 提供的文档很清楚。我看不出有什么需要用我自己的话来改写 clear 文档。 @hvd OP 想要一个速记,这正是这个答案所提供的。 @hvd 所以你不喜欢?? ?这里不是讨论这个特性在可读性方面是否有用的地方。 嗯,我认为在这种情况下,它仍然具有很强的可读性,如果您知道 ?? 运算符,理解它应该不是问题。【参考方案2】:

这是惰性初始化模式,因此直接的方法是使用 Lazy<T> 类。

class Foo

    Lazy<List<Row>> _rows;

    public Foo()
    
        _rows = new Lazy(() => new List<Row>());
    

    public List<Row> Rows
    
        get  return _rows.Value; 
    

这还有一个额外的好处,就是它不会用初始化逻辑“污染”getter。

【讨论】:

虽然实例化一个列表很便宜,这很愚蠢。 我选择了 OP 在他的问题中使用的内容。我假设这只是一个例子,实际的物体更重。【参考方案3】:

我建议三元运算符

get 
  return _rows == null ? _rows = new List<Row>() : _rows;

或者既然空的List&lt;Row&gt; 不会带来太多开销,为什么不去掉显式的_row 字段并实现只读属性(C# 6.0 语法):

public IList<Row> Rows get; = new List<Row>();

【讨论】:

C#6 解决方案是必经之路。 对于 C# 6,我更喜欢将 new 一般放在构造函数中以便于调试。【参考方案4】:

这是一个更好的主意:防止 _rows 成为 null

让你的构造函数初始化变量:

public MyClass()

    this._rows = new List<Row>();

然后你的财产就是

get

    return this._rows;

确保如果需要“清除”变量,始终调用其Clear 方法或分配一个新的空列表,而不是分配null。如果您确实需要在整个课程中明确和一致,可以在方法中对该操作进行编码。

非常更合乎逻辑。如果你的变量不应该是null它不应该是null。它还巧妙地避免了条件和 getter 修改状态的问题。

【讨论】:

在像这里这样的空列表的情况下,没有太大的区别。然而,这并不总是正确的。如果对象的初始化需要更长的时间,并且您(作为库的作者)不确定调用程序是否曾经请求过它,该怎么办。那么这种方法会引入过多的开销,延迟加载是要走的路。 越简单越好。用户永远不必测试 null,您也不必记录它。至少对于 List(我同意 Lister)和第一个版本,除非您另有了解,否则它可能是过早的优化(或者自动化可以为您做的事情)。不要花时间去想它。 @MrLister 如果因为一些性能开销而需要延迟加载,那么这个问题选择不好用空列表初始化替换它;更合适的选择是MyExpensiveToInitializeClass。该答案适用于书面问题,并假设除了书面内容之外没有理由这样做。如果有,那就另当别论了。【参考方案5】:
List<Row> _rows;
public List<Row> Rows => _rows ?? (_rows = new List<Row>());

【讨论】:

【参考方案6】:

正如其他人所说,您可以在这种情况下使用空合并运算符。

get

    return _rows ?? (_rows = new List<Row>());

值得注意的是,ReSharper 擅长建议这种改变(他们称之为quick-fix)。

在您的示例中,它会在if 语句下添加一个小曲线。将鼠标悬停在上面会显示suggestion,说明如何更改/简化代码。

点击几下,更改就实现了。

【讨论】:

【参考方案7】:

例如这样:

get return _rows ?? (_rows = new List<Row>()); 

【讨论】:

【参考方案8】:

如果您希望您的代码表现得像您当前的代码一样,在访问属性时懒惰地初始化您的支持字段,那么是的,您可以缩短它。您可以重命名您的支持字段,因为回答已经使用?? 将所有内容放在一个表达式中,并且当您拥有该单个表达式时,请使用 C# 6 的新属性语法来避免编写 getreturn

List<Row>_;List<Row> Rows=>_??(_=new List<Row>());

希望在您达到这一点之前,您会看到您已经将易于理解的代码变成了您永远不想维护的可怕混乱。

只需保持您的代码原样即可。如图所示,您可以缩短它,但这并不能使它变得更好。

如果问题在于编写需要更多时间,因为您一遍又一遍地键入相同的代码,许多 IDE 提供了一些功能来插入模板、sn-ps 或他们使用的任何术语。这使您可以按照以下方式定义一些东西

Type Field;
public Type Property 
  get 
    if (Field == null) 
      Field = new Type();
    
    return Field;
  

然后您的编辑器将提示您输入特定的 Type、Field、Property,而无需每次都再次输入。

【讨论】:

【参考方案9】:
return _rows ?? (_rows = new List<Row>());

【讨论】:

或更短:返回_rows == null?新列表 : _rows @nosbor 这两个都继续返回一个列表的新实例,因为它们未分配 _rows,这与 OP 所要求的行为不同。【参考方案10】:

如果你真的想缩短它,我会删除多余的括号。

    get
    
        if (_rows == null)
            _rows = new List<Row>();

        return _rows;
    

【讨论】:

最好不要,真的 我不确定为什么这会被否决。当然,除了缩短源代码之外,它实际上并没有做任何事情,但仍然如此。许多人认为从一个语句中删除花括号 if 语句是一件好事。 @MrLister 这并没有更好。这是相同的代码。 @PCLuddite 它肯定更短,不是吗? :) 这是最好的答案【参考方案11】:

您可以通过以下任何一种方式做到这一点:

条件运算符 (?:) 空合并运算符 (??)

带条件运算符

get 
  return _rows == null ? new List<Row>() : _rows;

空合并运算符

get  
  return _rows ?? new List<Row>(); 

【讨论】:

以上是关于有没有一种速记方法来返回可能为空的值?的主要内容,如果未能解决你的问题,请参考以下文章

有没有更优雅的方法来添加可为空的整数?

收集可能为空的值

PHP中判断变量值是不是为空的问题

为啥 C# 中的 async/await 会返回可为空的值,即使被告知不要这样做?

在 Typescript 中处理嵌套的可能为空的值

检查表单元素的值是否为空