如果变量为 null,则不执行任何操作的赋值操作

本文关键字:操作 任何 执行 赋值 变量 null 如果 | 更新日期: 2023-09-27 18:33:04

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

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

如果变量为 null,则不执行任何操作的赋值操作

已更新

从 C# 8 开始,这可以通过 Null 合并赋值运算符来实现

a ??= b;

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

原始答案

虽然语法很啰嗦

(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; },我只会使用一个衬里

如果您担心在单个语句中完成所有这些操作,那么您就不走运了 - 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参数传递。

编辑:虽然上述类似于Interlocked.CompareExchange,但这样的替代返回第一个参数的原始值,因此它可能比实现上述方法更麻烦。

或者你可以小心地重写你的初始语句,在初始赋值中使用空合并(??(运算符。

正如你所说,你需要一个if语句。没有条件运算符在null时不分配。在这种情况下,if最合适(并非所有内容都必须是单行(。

最佳选择:

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;
        }
    }
}

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

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

object a = null ?? something;

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

情况 2:切勿设置 a ,但在使用a时使用??。在这种情况下,代码将是:

MethodTakingA(a ?? b);

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

还有第三种情况,我做了你避免的确切任务。也就是说,当我的方法的一个参数可能为 null 时,我有一个默认值在这种情况下使用(而不是抛出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;

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

我认为 C# 8.0 附带的新功能使这变得非常简单。有一个新的??= 检查变量的运算符,如果它为 null,则设置值,如果不是,则不算。

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

只是在做

variable ??= expression;

在您的情况下,它是:

a ??= b;

作为一个语句,

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

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

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


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

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

作为上述内容的更短版本。同样,仅当a null时,才会评估a = b