あれ?って思ったことシリーズ第二弾。
つい先日、Cで書かれた組み込み機器と通信するプログラムをC#で書きました。
で、テストを書くときに、@neueccさんのChaining Assertionを使いました。
これは、Assert書くのを気持ちよくしてくれるライブラリです。
こんな感じ。
int actual = 0; //普通はこう書くけど Assert.AreEqual(actual, 0); //こんな感じで書ける actual.Is(0);
int以外の場合にコンパイルエラー
冒頭でも書きましたが、その日の僕はCで書かれた組み込み機器と通信するプログラムを作っていました。受け渡しするデータはBYTE、WORD、DWORDなわけですよ。で、C#側でもbyte、ushort、uintって出てくるわけですね。で、それに対して次のようなコードを書くとですね・・・
byte actual = 0; actual.Is(0);
えー、なんでやねん・・・ってことになります。
Chaining Assertionの定義はこんな感じです。
publicstaticvoid Is<T>(this T actual, T expected, string message = "")
これって、第一引数のTが既にbyteだから第二引数の0に関してはbyte扱いしてくれても良さそうですよね。っていうか、これ実は拡張メソッドでなければコンパイル通るんです。
void Func<T>(T t1, T t2) { } [TestMethod] void Test() { byte actual = 0; //これはOK。ほら、分かってるじゃん! Func(actual, 0); }
でも、拡張にしたらコンパイルエラー
publicstaticclass Ex { publicstaticvoid Func<T>(this T t1, T t2) { } } [TestClass] publicclass ExTest { [TestMethod] void Test() { byte actual = 0; //コンパイルエラー//分かっておくれよ・・・ actual.Func(0); } }
しかも、これint以外は全滅かと思いきや、いけるのもあるw。なんでlongとかいけてるんだろ?
×がついているのはコンパイルエラーになります。
//×byte by = 0; by.Is(0); //×short s = 0; s.Is(0); //×ushort us = 0; us.Is(0); int i = 0; i.Is(0); //×uint ui = 0; ui.Is(0); long l = 0; l.Is(0); //×ulong ul = 0; ul.Is(0); float f = 0; f.Is(0); double d = 0; d.Is(0); decimal dec = 0; dec.Is(0);
まあ、数値を型にするの色々ルールあるし、なんかあるのかなー。できるだけintにするとかね。
で、新たな拡張作って、回避しました。
まあ、キャストするとかあるんですけど、折角Chaining Assertion使ってるのに、そんなの気持ちよくない。
今回は以下のような拡張メソッド作って回避しました。
拡張メソッドはより適合する方を選択してくれます。
publicstaticclass NumericAssertEx { publicstaticvoid Is(thisbyte actual, int expected, string message = "") { Assert.IsTrue(byte.MinValue <= expected && expected <= byte.MaxValue); Assert.AreEqual(actual, (byte)expected, message); } publicstaticvoid Is(thisshort actual, int expected, string message = "") { Assert.IsTrue(short.MinValue <= expected && expected <= short.MaxValue); Assert.AreEqual(actual, (short)expected, message); } publicstaticvoid Is(thisushort actual, int expected, string message = "") { Assert.IsTrue(ushort.MinValue <= expected && expected <= ushort.MaxValue); Assert.AreEqual(actual, (ushort)expected, message); } publicstaticvoid Is(thisuint actual, int expected, string message = "") { Assert.IsTrue(0<= expected); Assert.AreEqual(actual, (uint)expected, message); } publicstaticvoid Is(thisulong actual, int expected, string message = "") { Assert.IsTrue(0<= expected); Assert.AreEqual(actual, (ulong)expected, message); } }