ソースの表示以前のリビジョンバックリンク全て展開する/折り畳む文書の先頭へ Share via Share via... Twitter LinkedIn Facebook Pinterest Telegram WhatsApp Yammer Reddit Teams最近の変更Send via e-Mail印刷パーマリンク × « PowerEdge T330/PERC H330環境でPVE 8がEFI stubうんちゃらで起動できない件 Windowsのローカルユーザーのパスワードをネットワーク越しに変更する » C#の0がenum値に暗黙キャストされてinvokeStaticで例外出て超ハマった 僕はついさっき知ったばかりなんですけど、C#の数値0ってあらゆるenum値に暗黙的キャストされるんですってね。要するに、0だけはキャスト不要で列挙型の値として変数に代入できちゃったりするということ。コードで示すと以下のようなかんじ。 using System; public class Program { enum Hoge { A, B } enum Piyo { C, D } public static void Main() { Hoge h1 = 0, h2 = 0.0f; Piyo p1 = 0x0, p2 = 0.0m; Console.WriteLine($"{h1}, {h2}"); Console.WriteLine($"{p1}, {p2}"); } } このコードは何の警告もなくビルドが通り、以下の実行結果が得られる。 A, A C, C 浮動小数点数やDecimalのゼロも同様の扱いっていうのが、なかなかのキモいポイント。C#のenumってC/C++のそれと違ってそれなりに厳密なので、自分的には結構衝撃的な仕様だった。まぁ、分かってしまえばどうってことはない。 で、ここからが本題。 そんな0のenumへの暗黙的型変換のおかげで、意図せぬメソッドのオーバーロード解決が行われ、小一時間ハマった。 以下のような処理メソッドAddと、それに対する単体テストAddTestを考える。対象メソッドはプライベートな静的メソッドなので、テスト呼び出しにはPrivateType.InvokeStaticメソッドを使っているが、至ってシンプルなコードである。 何の疑いもなく動くと思いきや、ケース3のテストでのみMissingMethodException例外が発生し、Addメソッドの呼び出しに失敗するのだ! // 加算 private static decimal? Add(decimal? v1, decimal? v2) { return v1 + v2; } // Addメソッドの単体テスト void AddTest() { var privateType = new PrivateType(typeof(AddClass)); // ケース1 var value = (decimal?)privateType.InvokeStatic("Add", 1.0m, 1.0m); // OK Assert.AreEqual(2, value); // ケース2 value = (decimal?)privateType.InvokeStatic("Add", 1.0m, 0.0m); // OK Assert.AreEqual(1, value); // ケース3 value = (decimal?)privateType.InvokeStatic("Add", 0.0m, 1.0m); // MissingMethodException例外が発生! Assert.AreEqual(1, value); } MissingMethodExceptionは読んで字のごとく、メソッドが見つからなかった場合に投げられる例外だ。 ケース1~2は通っているのに見つからないとは一体…!?という感じなのだが、これまでの説明からお気づきであろう、ケース1~2と3では呼び出されるInvokeStaticのシグネチャが違うのだ。同メソッドは引数違いで10個ほどのオーバーロードが定義されており、それぞれ以下のシグネチャのものが呼び出される。 ケース1~2 InvokeStatic(String, params Object[]) ケース3 InvokeStatic(string, System.Reflection.BindingFlags, params Object[]) ケース3では、第二引数の0.0mがBindingFlagsに暗黙キャストされた結果、引数を1つ持つAddメソッドを呼び出そうとして例外を吐くというわけ。なんじゃそりゃー!分かるわけネェーッ!!意図通り動かすにはprivateType.InvokeStatic(“Add”, (decimal?)0.0m, 1.0m)のように、明示的にキャストし正しいオーバーロード解決を導いてあげればよい。 この暗黙型変換を禁止ないし警告出してくれるコンパイルオプションとかないのかしら…ハマった時に死ぬほどわかりづらいんですけど…… Comments Name E-Mail Website 人間の証明として、ボックス内の全ての文字を入力してください。 この項目は空のままにして下さい:Preview Comment blog/2023/2023-11-18.txt 最終更新: 2023-11-18 23:42by Decomo