【C# .NET10 Preview3】null 条件付き代入

# はじめに

[.NET10 Preview3](https://dotnet.microsoft.com/ja-jp/download/dotnet/10.0) が利用可能になりました。  
月１更新だと思うのですが、なんか毎回１週間前倒しになってるような・・・

[リリースノート](https://github.com/dotnet/core/discussions/9846)によれば色々と機能追加されています。  
今回はそのうちの C# 機能のひとつ [`null` 条件付き代入](https://github.com/dotnet/core/blob/main/release-notes/10.0/preview/preview3/csharp.md#null-conditional-assignment)を見ていきます。

## サンプルコード

<details>
<summary>サンプルコード</summary>

```cs
using System.Linq.Expressions;
using Xunit;

file class Customer
{
    public string Name { get; set; } = "John";
    public int Age { get; set; } = 20;
}

public class __NullConditionalAssignmentTest
{
    [Fact]
    void HowToUse()
    {
        var customer = new Customer();
        customer?.Name = "Bob";
        customer?.Age = 30;
        Assert.Equal("Bob", customer!.Name);
        Assert.Equal(30, customer!.Age);

        Customer? nullCustomer = null;
        nullCustomer?.Name = "Alice"; // 何もしない
        nullCustomer?.Age = 25; // 何もしない
        Assert.Null(nullCustomer?.Name);
        Assert.Null(nullCustomer?.Age);

        // 従来の書き方
        if (nullCustomer is not null)
        {
            nullCustomer.Name = "Charlie";
            nullCustomer.Age = 35;
        }
    }

#if false
    void Exp()
    {
        // 式ツリーのラムダに null 伝搬演算子を含めることはできません。CS8072
        // 式ツリーは、代入演算子を含むことはできませんCS0832
        Expression<Action<Customer?, string>> setNameExpression =
            (customer, name) => customer?.Name = name;
    }
#endif
}
```

</details>

## `null` 条件付き代入

```cs
file class Customer
{
    public string Name { get; set; } = "John";
    public int Age { get; set; } = 20;
}

var customer = new Customer();
customer?.Name = "Bob";
customer?.Age = 30;
Assert.Equal("Bob", customer!.Name);
Assert.Equal(30, customer!.Age);

Customer? nullCustomer = null;
nullCustomer?.Name = "Alice"; // 何もしない
nullCustomer?.Age = 25; // 何もしない
Assert.Null(nullCustomer?.Name);
Assert.Null(nullCustomer?.Age);

// 従来の書き方
if (nullCustomer is not null)
{
    nullCustomer.Name = "Charlie";
    nullCustomer.Age = 35;
}
```

これまでは `obj?.Value = 1` みたいなのはコンパイルエラーになっていたのですが、代入できるようになりました。用途は割と限定的ですが、`nullable` の登場で需要があったんだと思います。

## 式木には使えない

```cs
// 式ツリーのラムダに null 伝搬演算子を含めることはできません。CS8072
// 式ツリーは、代入演算子を含むことはできませんCS0832
Expression<Action<Customer?, string>> setNameExpression = (customer, name) =>
    customer?.Name = name;
```

**式木**には使えません。これがなぜか考えてみると、`null` 許容演算子は糖衣構文で１行の式ではなさそうです。

上の使用例を逆コンパイルしてみます。

```cs
<コンパイラによる名前>__Customer customer = new <コンパイラによる名前>__Customer();
if (customer != null)
{
	customer.Name = "Bob";
}
if (customer != null)
{
	customer.Age = 30;
}
Assert.Equal("Bob", customer.Name);
Assert.Equal<int>(30, customer.Age);
<コンパイラによる名前>__Customer nullCustomer = null;

// if 文に置き換わっている！
if (nullCustomer != null)
{
	nullCustomer.Name = "Alice";
}
// if 文に置き換わっている！
if (nullCustomer != null)
{
	nullCustomer.Age = 25;
}
Assert.Null((nullCustomer != null) ? nullCustomer.Name : null);
Assert.Null<int>((nullCustomer != null) ? new int?(nullCustomer.Age) : null);
bool flag = nullCustomer != null;
if (flag)
{
	nullCustomer.Name = "Charlie";
	nullCustomer.Age = 35;
}
```

コンパイル結果は `if` 文による複数行のコードになるため、これは式木には使えません。式木関係は複雑な機能なので、基本的に式木には機能追加しない方針なのでしょう。

## おわりに

今回取り上げた追加機能はかゆところに手が届く系のものでした。  
Preview3 は他にいくつか面白い追加機能があるため、追って取り上げようと思います。

[.NET10 Preview3](https://dotnet.microsoft.com/ja-jp/download/dotnet/10.0)

関連  
[【C#】.NET 10 Preview 1 ｷﾀ━━(ﾟ∀ﾟ)━━!!](https://qiita.com/abetakahiro123/items/2153df9795cd0f12da47)  
[【C# .NET 10 Preview 1】値型の配列をスタックに作成する最適化の検証](https://qiita.com/abetakahiro123/items/9113ee6347580c5d0e39)  
[【C# .NET 10 Preview 2】参照型がスタックに置かれる最適化](https://qiita.com/abetakahiro123/items/cedd77ba25de1b8db8d1)