あれ?って思ったことシリーズ第二弾。
つい先日、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の定義はこんな感じです。
public static void 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); }
でも、拡張にしたらコンパイルエラー
public static class Ex { public static void Func<T>(this T t1, T t2) { } } [TestClass] public class 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使ってるのに、そんなの気持ちよくない。
今回は以下のような拡張メソッド作って回避しました。
拡張メソッドはより適合する方を選択してくれます。
public static class NumericAssertEx { public static void Is(this byte actual, int expected, string message = "") { Assert.IsTrue(byte.MinValue <= expected && expected <= byte.MaxValue); Assert.AreEqual(actual, (byte)expected, message); } public static void Is(this short actual, int expected, string message = "") { Assert.IsTrue(short.MinValue <= expected && expected <= short.MaxValue); Assert.AreEqual(actual, (short)expected, message); } public static void Is(this ushort actual, int expected, string message = "") { Assert.IsTrue(ushort.MinValue <= expected && expected <= ushort.MaxValue); Assert.AreEqual(actual, (ushort)expected, message); } public static void Is(this uint actual, int expected, string message = "") { Assert.IsTrue(0 <= expected); Assert.AreEqual(actual, (uint)expected, message); } public static void Is(this ulong actual, int expected, string message = "") { Assert.IsTrue(0 <= expected); Assert.AreEqual(actual, (ulong)expected, message); } }