【问题标题】:Assignment operation that does nothing if variable is null?如果变量为空,赋值操作什么都不做?
【发布时间】:2015-02-02 13:02:38
【问题描述】:

如果变量已经是null,我想有条件地为变量赋值。此外,如果该变量还不是 null 我希望 nothing 发生,并且我希望能够使用单个运算符完成所有操作。

object a = null;
object b = new Something();

// this is essentially what I want but done with an operator:
if(a == null)
{
    a = b;
}

// this is all I feel I have to work with,
a = a || b;
a = a ?? b;
a = a == null ? b : a;

// the above methods all end up performing a = a if a is not null

【问题讨论】:

  • 所以我认为a = a ?? b; 已经出局了?
  • @JoelRondeau 看起来如此 - OP 不希望 a 被分配回自己。
  • 那么你必须使用if 来避免分配。我没有看到任何其他选择。无论如何,做a = a 应该不是问题,除非setter 有一些庞大的逻辑。
  • 你说 a = a ?? b 之类的东西最终会执行 a = a 如果 a 不为空,但你没有解释为什么这是一个问题。
  • 你有不想要a = a的理由吗?这对您的应用程序是否有害?

标签: c# c#-4.0 variable-assignment


【解决方案1】:

更新

从 C# 8 开始,这可以通过 Null-coalescing assignment 运算符实现

a ??= b;

这只会在anull 时将b 分配给a

原答案

虽然语法很冗长

(a is null?()=>a=b:(Action)(()=>{}))();

让我们分开

(                           // Expression starts here
    a is null               // If a == null...
        ? () => a = b       // return lambda that assigns a = b
        : (Action) (        // Else return next lambda casted as Action
            () => {}        // Empty lambda that does nothing
        )                   // End cast
)                           // Expression ends here
();                         // Execute it!

无论如何,如果if(a is null) { a = b; } ,我只会使用一个衬里

【讨论】:

  • 为什么投反对票?这完全符合 OP 的要求
  • 我没有投反对票,但这只是if 的一种晦涩难懂的方式。事实上,? 就是这样做的。
  • 区别是什么?返回必须分配给某个变量的表达式。摆脱该变量的唯一方法是让它返回 lambdas 并像 javascript 自执行函数一样执行它
  • 我没有投反对票,这是一个有趣的 hack,但我认为代码最终有点神秘。
  • 我肯定会做一个如果,但我必须发布完整的答案
【解决方案2】:

如果您担心在单个语句中完成所有这些操作,那么您就不走运了 - C# 在语言级别上没有此功能,并且不支持运算符声明(如 F#)或重载赋值运算符(与 C++ 一样)。但是,如果没有您要求的那么优雅,也有几个选项。

if 语句,正如你提到的,虽然它可以写成一行

if(a == null) a = b;

使用ref 参数的辅助方法

public void AssignIfNull<T>(ref T target, T value)
{
    if(target == null) target = value;
}

// ...

AssignIfNull(ref a, b);

请注意,上述内容适用于属性,因为它们不能作为ref 参数传递。

EDIT:虽然上面类似于Interlocked.CompareExchange,但这种替代方法返回第一个参数的 original 值,因此它可能比实现上述方法。

或者您可以仔细重写您的初始语句以在初始分配中使用空合并 (??) 运算符。

【讨论】:

    【解决方案3】:

    正如您所说,if 声明是您所需要的。 null 时没有不赋值的条件运算符。在这种情况下,if 是最合适的(并非所有内容都必须是 oneliner)。

    最佳选择:

    if(a == null)
    {
        a = b;
    }
    

    或者:

    a = a ?? b;
    

    事实上,我相信后者被优化为一个简单的if 语句。

    a 分配给自己也不错。对于对象引用,它只是内存地址的分配。对于值类型,这只是一小部分数据。

    如果a 实际上是一个属性设置器,请检查设置器内部的值是否发生了变化:

    private string a;
    
    public string A
    {
        get
        {
            return a;
        }
        set
        {
            if (value != a)
            {
                a = value;
            }
        }
    }
    

    【讨论】:

    • a = a 是否仍会触发对象的set 方法?我真的不想触发它。
    • 哦,对不起,我的错。 :)
    【解决方案4】:

    我自己也遇到过这种情况,我决定检查我处理这个问题的代码。我倾向于有两种解决方案,都涉及空合并运算符。

    案例 1:初始化。使用上面的值,这将变为:

    object a = null ?? something;
    

    显然,我不会写那行代码(如果没有别的,resharper 会抱怨)。但它是正在发生的事情的本质。如果我在创建 a 时有两个(或更多)值可用,那么我会这样写。

    案例 2:从不设置 a,但在使用 a 时使用 ??。在这种情况下,代码将是:

    MethodTakingA(a ?? b);
    

    如果有多个方法调用或我需要使用?? 的其他地方,那么这是一个坏主意。

    在第三种情况下,我会执行您正在避免的确切任务。那是当我的方法的一个参数可能为空时,我有一个在这种情况下使用的默认值(而不是抛出ArgumentNullException)。这是一个例子:

    public void Foo(string str)
    {
      str = str ?? String.Empty;
      //Use str as needed below without fear that it might be null.
    }
    

    对于这种情况,我想要一个更好的答案,但我肯定不会编写值得微优化该分配的代码,以及

    的理论答案
    string localStr = str ?? String.Empty;
    

    只是添加一个新变量来添加一个。否则它对我没有任何用处,所以我保持我的自我分配并接受它。

    【讨论】:

      【解决方案5】:

      我认为 C# 8.0 附带的新功能使这变得非常容易。有一个新的??= operators 可以检查变量,如果它为空,则设置值,如果不是什么都没有。

      if (variable is null)
      {
         variable = expression;
      }
      

      只是在做

      variable ??= expression;
      

      在你的情况下是:

      a ??= b;
      

      【讨论】:

        【解决方案6】:

        作为一个单一的陈述,

        var result = ((a == null) ? (a = b) : null);
        

        然后可以丢弃result 的值。将ab 作为对象属性,并在a 的设置上添加Console.WriteLine() 将显示它仅在之前为空时分配。

        阻止它完全干净的唯一原因是创建了一个一次性变量result;希望它仍然足够干净。


        附录 - 我刚刚意识到你也可以使用:

        var result = a ?? (a = b);
        

        作为上述的更短版本。同样,a = b 仅在 anull 时才被评估。

        【讨论】:

          猜你喜欢
          • 2012-02-15
          • 2016-01-05
          • 2020-07-08
          • 2021-09-14
          • 2017-10-30
          • 2019-07-06
          • 1970-01-01
          • 1970-01-01
          • 2017-07-03
          相关资源
          最近更新 更多