変数名の後ろに"!" がある - null 免除演算子の利用
C#のコードで、変数名の後ろに!記号がある場合の意味を紹介します。
概要
変数名の後ろの
!
記号はnull免除演算子(null-forgiving operator)と呼ばれます。
null免除演算子
null免除演算子
!
をnull許容参照型の変数名の後ろに記述すると、その変数は null でないとみなされます。
例1
UI
下図のフォームを作成します。
コード
下記コードを記述します。
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 NullForgivingDemo
{
public partial class FormSimpleNullForgiving : Form
{
public FormSimpleNullForgiving()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string str = "ぺんぎんクッキー";
string? newstr = "かるがもタルト";
if (new Random().NextDouble() < 0.5) newstr = default;
str = newstr;
textBox1.Text = str;
}
}
}
ビルド
ビルドを実行すると、下記のワーニングが発生します。
メッセージ
CS8600:Null リテラルまたは Null の可能性がある値を Null 非許容型に変換しています。
解説
確率でstring型のstr変数にnull許容型のstring型のdefault値を代入する処理です。
string?
型のdefault値はnullとなるため、string型にnullを代入してしまう場合があります。
このため、コンパイル時に先のワーニングが発生します。
string? newstr = "かるがもタルト";
if (new Random().NextDouble() < 0.5) newstr = default;
str = newstr;
null 免除演算子を利用したワーニングの回避方法
以下のコードに修正すると、ワーニングが発生しなくなります。
private void button1_Click(object sender, EventArgs e)
{
string str = "ぺんぎんクッキー";
string? newstr = "かるがもタルト";
if (new Random().NextDouble() < 0.5) newstr = default;
str = newstr!;
textBox1.Text = str;
}
解説
newstr!
の記述により、newstr変数はnullでないとみなされます。そのため、ワーニングは発生しなくなります。
一方で実行時の動作は変わらないため、元のコードと動作は全く同じになります。
str = newstr!;
実行結果
プロジェクトをビルドすると、上記のアラートは発生しないことが確認できます。
プロジェクトを実行します。下図のウィンドウが表示されます。
[button1]をクリックします。下部のテキストボックスに「かるがもタルト」の文字列が表示されるか、テキストボックスが空白になるかの動作をします。
乱数による動作のため、クリックするごとにどちらかの表示になります。
補足1
なお、nullが代入される状況が発生しない下記のコードではワーニングは発生しません。
private void button2_Click(object sender, EventArgs e)
{
string str = "ぺんぎんクッキー";
string? newstr = "かるがもタルト";
str = newstr;
textBox1.Text = str;
}
補足2
null 許容値型の場合は、
!
を記述してもビルドエラーになります。
private void button3_Click(object sender, EventArgs e)
{
int value = 100;
int? newvalue = 56;
if (new Random().NextDouble() < 0.5) newvalue = null;
value = newvalue!;
textBox1.Text = value.ToString();
}
次のエラーが発生します。
エラーメッセージ
CS0266: 型 'int?' を 'int' に暗黙的に変換できません。明示的な変換が存在します (cast が不足していないかどうかを確認してください)
例2
次のプログラムを作成します。
UI
コード
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 NullForgivingDemo
{
public partial class FormSimpleNullForgiving : Form
{
class MyItem
{
public int id = -1;
public string name = "ぺんぎんクッキー";
}
public FormSimpleNullForgiving()
{
InitializeComponent();
}
private void button4_Click(object sender, EventArgs e)
{
MyItem? item = null;
textBox1.Text = String.Format("名前 : {0}", item.name);
}
}
}
ビルド
ビルドを実行すると、下記のワーニングが発生します。
メッセージ
CS8602: null 参照の可能性があるものの逆参照です。
解説
nullになるMyItemクラスのメンバ変数を参照する処理です。
MyItem?
型のitem変数はnullとなるため、item.nameは NullPointerExceptionとなり、コンパイル時に先のワーニングが発生します。
MyItem? item = null;
textBox1.Text = String.Format("名前 : {0}", item.name);
null 免除演算子を利用したワーニングの回避方法
以下のコードに修正すると、ワーニングが発生しなくなります。
private void button4_Click(object sender, EventArgs e)
{
MyItem? item = null;
textBox1.Text = String.Format("名前 : {0}", item!.name);
}
実行結果
プロジェクトをビルドして実行し、button4をクリックします。nullのメンバ変数にアクセスしたため、例外が発生します。
下記例外が発生します。
エラーメッセージ
System.NullReferenceException
HResult=0x80004003
Message=Object reference not set to an instance of an object.
Source=NullForgivingDemo
!
null 免除演算子を記述してもワーニングの発生を抑えられるだけで、nullのオブジェクトにアクセスしたことによる例外は回避できません。
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用