Debug.Asesrtはプログラム開発中にロジックのエラーを識別するために使われることが多いメソッドです。Debug.Asesrt(Boolean)の引数がfalseなら、メッセージを出力し呼び出しスタックを表示してくれます。デフォルトでは、Debug.Assertメソッドはデバッグビルドのみで機能します。
さて、このDebug.Assertメソッドを使ったこんなプログラムを用意します。
using System.Diagnostics;
public static class Program
{
public static void Main(string[] args)
{
SayHello(" ");
}
static void SayHello(string name)
{
Debug.Assert(!string.IsNullOrWhiteSpace(name));
Console.WriteLine($"Hello, {name}!");
}
}
.NET 8まではこれを実行すると、次のように表示されました。アサーションに失敗したことと、そのアサーションのプログラムのパスと行数がわかるので、便利ですね。
Process terminated. Assertion failed.
at Program.SayHello(String name) in 「ここにプログラムのパスと行数」
at Program.Main(String[] args) in 「ここにプログラムのパスと行数」
.NET 9から、これがさらに便利になって、条件をデフォルトで表示してくれるようになりました。先ほどのプログラムを.NET 9で実行すると、「!string.IsNullOrWhiteSpace(name)」というアサーションの条件を追加して表示してくれます。
Process terminated. Assertion failed.
!string.IsNullOrWhiteSpace(name)
at Program.SayHello(String name) in 「ここにプログラムのパスと行数」
at Program.Main(String[] args) in 「ここにプログラムのパスと行数」
.NET 9より前でも、「Assert(Boolean, String)」を使えば、同じようなことができますが、多少面倒ですね。
static void SayHello(string name)
{
Debug.Assert(!string.IsNullOrWhiteSpace(name), "!string.IsNullOrWhiteSpace(name)");
Console.WriteLine($"Hello, {name}!");
}
デバッグビルドのIL(中間言語)を見て、どういう仕組みになっているか確認してみます。次のコードは、SayHelloメソッドのILです。
.method private hidebysig static void
SayHello(
string name
) cil managed
{
.maxstack 8
// [11 5 - 11 6]
IL_0000: nop
// [12 9 - 12 92]
IL_0001: ldarg.0 // name
IL_0002: call bool [System.Runtime]System.String::IsNullOrWhiteSpace(string)
IL_0007: ldc.i4.0
IL_0008: ceq
IL_000a: ldstr "!string.IsNullOrWhiteSpace(name)"
IL_000f: call void [System.Runtime]System.Diagnostics.Debug::Assert(bool, string)
IL_0014: nop
// [13 9 - 13 46]
IL_0015: ldstr "Hello, "
IL_001a: ldarg.0 // name
IL_001b: ldstr "!"
IL_0020: call string [System.Runtime]System.String::Concat(string, string, string)
IL_0025: call void [System.Console]System.Console::WriteLine(string)
IL_002a: nop
// [14 5 - 14 6]
IL_002b: ret
} // end of method Program::SayHello
「IL_000a: ldstr "!string.IsNullOrWhiteSpace(name)"」に注目してください。
.NET 9では、こんな感じでアサーションが失敗した場合に表示する文字列が、IL内に定義されています。そして、よくILを見てみるとDebug.Asesrt(Boolean)ではなくて、「Assert(Boolean, String)」が呼ばれています。
実は、「Debug.Asesrt(Boolean)」ではなく「Assert(Boolean, String)」が呼ばれているのです。
- 「Debug.Asesrt(Boolean)」には、.NET 9から追加された「OverloadResolutionPriority」が付与されている。これにより、オーバーロード解決の優先度が下がっている。コードはこちら。
- 「Assert(Boolean, String)」の第二引数messageに、CallerArgumentExpressionAttributeを付与して、デフォルト引数としてnullを設定。コードはこちら
この2個を組み合わせた時、
Debug.Assert(!string.IsNullOrWhiteSpace(name));
という呼び出しをした時に、「Assert(Boolean, String)」が呼ばれ、「"!string.IsNullOrWhiteSpace(name)"」という文字列が第二引数に渡されます。
その結果、.NET 9からDebug.Assertがアサーションの条件をデフォルトで表示してくれるようになっています。
Debug.Asesrt、.NET 9で進化して、雑に使った時に親切になってナイスですね。