?? 演算子の意味 (null 合体演算子) - はてな"?"記号が2つ続く演算子の意味 - C#

概要

?? 演算子の意味と動作を紹介します。
?? 演算子は「null 合体演算子」と呼ばれており、nullの場合の処理を分けることができます。

「はてな"?"記号が2つ続く演算子」は、旧いC#にはない記述法のため、突然出てくると理解が難しいことがあります。
メモ
??= 演算子についてはこちらの記事を参照してください。

書式

(値1 または 変数1) ?? (値2 または 変数2)

意味

??演算子の左辺 (値1、変数1) の値が null の場合、演算子の右辺 (値2、変数2) の値を返します。

記述例

text変数がnullの場合、空文字列を返します。
text ?? "";

price変数がnullの場合、price_z変数の値を返します。
price ?? price_z;

if文での書き換え

下記コードは次のif文で書き換えられます。
??演算子のコード
int? value1;
int value2;
int value3;

value3 = value1 ?? value2;
if文で書き換えたコード
int? value1;
int value2;
int value3;

if (value1 == null){
  value3 = value2;
}else{
  value3 = (int)value1;
}

/*
//または
if (value1 == null) value3 = value2; else value3 = (int)value1;
*/

プログラム例1

以下のプログラムを作成します。

UI

下図のフォームを作成します。ラジオボタン、テキストボックス、ボタンを配置します。

コード

using System;
using System.Windows.Forms;

namespace NullOperatorDemo
{
  public partial class FormNullCoaleescingAssignmentOperator : Form
  {
    public FormNullCoaleescingAssignmentOperator()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      int? value;

      if (radioButton1.Checked == true) {
        value = 320;
      }
      else {
        value = null;
      }

      textBox1.Text = string.Format("値は:{0:d}です", value ?? -1);
    }
  }
}

解説

radioButtonの選択状態によって value 変数に代入する値を変えます。radioButton1 がチェックされている場合は320を代入しますが、 radioButton2がチェックされている場合は null を代入します。
  int? value;

  if (radioButton1.Checked == true) {
    value = 320;
  }
  else {
    value = null;
  }

テキストボックスに値を表示します。value ?? -1 の記述のため、value の値がnullの場合は -1の値が返され「値は:-1です」のメッセージがテキストボックスに表示されます。 value の値がnullでない場合はvalueに代入された320の値が返されますので、「値は:320です」のメッセージがテキストボックスに表示されます。
  textBox1.Text = string.Format("値は:{0:d}です", value ?? -1);

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。


[radioButton1]が選択された状態で[button1]ボタンをクリックします。"値は:320です"のメッセージが表示されます。


[radioButton2]をクリックしてチェックを切り替えます。チェックを切り替え後[button1]ボタンをクリックします。


"値は:-1です"のメッセージが表示されます。

プログラム例2

(コード

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace BasicOperator
{
  public partial class FormMain : Form
  {
    public FormMain()
    {
      InitializeComponent();
    }

    public string GetValueData(int index)
    {
      switch (index) {
        case 0:
          return "Penguin";
        case 1:
          return null;
        case 1:
          return "Whale";
      }
    }

    private void button1_Click(object sender, EventArgs e)
    {
      string value;

      value = GetValueData(0);
      textBox1.Text += string.Format("{0:d}\r\n", value.Length);

      value = GetValueData(1);
      textBox1.Text += string.Format("{0:d}\r\n", value.Length);

      value = GetValueData(2);
      textBox1.Text += string.Format("{0:d}\r\n", value.Length);
    }

    private void button2_Click(object sender, EventArgs e)
    {
      string value;

      value = GetValueData(0);
      textBox1.Text += string.Format("{0:d}\r\n", (value ?? "").Length);

      value = GetValueData(1);
      textBox1.Text += string.Format("{0:d}\r\n", (value ?? "").Length);

      value = GetValueData(2);
      textBox1.Text += string.Format("{0:d}\r\n", (value ?? "").Length); 
    }
  }
}

解説

??演算子は左辺がnullであった場合は右辺の値を返します。

  string value = "duck" ?? "penguin";
上記の場合は左辺がnullではないためvalueの値は"duck"です。

  string value = null ?? "penguin";
上記の場合は左辺がnullのためvalueの値は"penguin"です。

補足

??演算子を使わずにif文で記述した場合のコード例です。??演算子を使ったほうがシンプルに記述できることがわかります。
private void button1_Click(object sender, EventArgs e)
{
  string value;

  value = GetValueData(0);
  if (value != null) {
    textBox1.Text += string.Format("{0:d}\r\n", value.Length);
  }
  else {
    textBox1.Text += "0\r\n";
  }

  value = GetValueData(1);
  if (value != null) {
    textBox1.Text += string.Format("{0:d}\r\n", value.Length);
  }
  else {
    textBox1.Text += "0\r\n";
  }

  value = GetValueData(2);
  if (value != null) {
    textBox1.Text += string.Format("{0:d}\r\n", value.Length);
  }
  else {
    textBox1.Text += "0\r\n";
  }
}

使用例

CS8601 : Possible null reference assignment. CS8601 : Null 参照代入の可能性があります。 のワーニングを回避するために使用するケースもあります。

IConfigurationオブジェクトのGetConnectionString()メソッドの戻り値は string? 型のため、null非許容型の string型に代入すると、CS8601のワーニングが発生します。
このため、GetConnectionString()メソッドの戻ちりがnullであった場合は、空文字列 "" を 代入したいです。if 文でも記述できますが、?? を利用したほうが簡単に記述できます。
CS8601 ワーニングが出る状態
private IConfiguration _conf;
...
string connectionStr = _conf.GetConnectionString("DBConnectString");
CS8601 ワーニングを解消した状態
private IConfiguration _conf;
...
string connectionStr = _conf.GetConnectionString("DBConnectString") ?? "";

?? 演算子を利用しない記述例
  string? temp_str = _conf.GetConnectionString("DBConnectString");
  if (temp_str == null) {
    connectionStr = "";
  }
  else {
    connectionStr = temp_str;
  }
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
掲載日: 2021-01-25
iPentec all rights reserverd.