#はじめに
データ分析のための前処理などをするときに、簡単な短いプログラムを作って実行したいという需要はあります。
それこそ、スクリプト言語と言われる、Perl、Ruby、Pythonなどが大活躍するといわれているアレです。
C#のような、コンパイルするタイプの言語だと、気軽に実行できない、余計なことを書かないといけないとか色々言われます。
とはいえ、Visual StudioにあるC#のTestProjectを使うと簡単にスクリプト実行っぽいことができます。
Visual Studio Communityは無料だし悪くない選択肢だと思います。
C#のスクリプト実行環境というとLINQPadが有名ですが、こっちは公式の強みってやつですかね。
#そもそもTestProjectとは
TestProjectとは、単体テストを行うためのプロジェクトです。
単体テストとは、クラスやメソッド単位で、プログラムが正しく挙動しているかどうかを調べるものです。
そのため、よくあるサンプルでは、
[TestMethod()]
public void AddTest()
{
int x = 0; // TODO: 適切な値に初期化してください
int y = 0; // TODO: 適切な値に初期化してください
int expected = 0; // TODO: 適切な値に初期化してください
int actual;
actual = Calculator.Add(x, y);
Assert.AreEqual(expected, actual);
}
このような感じで、メソッドを叩いて期待どうりの挙動をしているかをチェックするために使います。
僕も、はじめそういう感じで使うものだと思っていました。
入力と期待の返り値を考えるの面倒だわ、テスト駆動開発面倒だからスルーだわ、とか思っていました。
でも、ちょっと使ってみると、それだけではないことがわかります。
#メソッド単位の実行・デバック
TestProjectは、Visual Studioから、メソッド単位の実行ができます。TestProjectの[TestClass]の属性がついたクラスで、[TestMethod()]属性がついたメソッドは、Visual Studioから実行できます。
対象となるソースコード上で、マウスで右クリックからのコンテキストメニューに「テストの実行」「テストの実行」
これをクリックすると、対象のメソッドが実行されます。
そして、メソッドの実行にかかった時間、そのメソッドの吐き出した、標準出力を見ることができます。
メソッド単位の実行ができて、標準出力が得られるなら、短いプログラムの実行環境としては最適で、スクリプト需要とマッチします。
テスト用の入力と返り値を考えなくても、標準出力で目視すれば、開発時には必要十分です。
(リリース後のアップデートの時には、適切にテストを作ってすべての機能のチェックをした方がいいと思いますが)
デバッグ実行もできるので、途中で止めて、その中の変数がどうなっているかも、通常のVisual Studioでの開発同様に見ることができます。
また、自動で実行にかかった時間も計測してくれるので、パフォーマンスの良し悪しもややこしいことをしなくてもわかります。
メソッド単位の実行のため、小さいプログラムのために複数のファイルを用意しなくても一つのファイルで管理できるのも利点でしょう。当然ですが、メソッドはクラスにいくつでも作ることできます。また、私は、メソッド名を日本語にしています。他から参照しないメソッドだから堂々と日本語です。クールな英語名なんて考えなくていいです。わかりやすいのが正義。
複数のスクリプトで同じように使いたい変数があるときがあります。そういうことにはTestの初期化を行ういいです。これは、実行時に必ず先に実行してくれるメソッドです。
[TestClass]
public class UnitTest1
{
[TestInitialize]
public void Init()
{
//共通して使う変数の初期化処理
}
}
#便利拡張メソッド
テストプロジェクトでのTestを簡単に行う拡張メソッドとして便利なのが、Chaining Assertionです。メソッドチェーンでテストを気持ちよく書けます。
また、標準出力のために、毎回System.Console.WriteLineを呼び出すのが面倒だったので、拡張メソッドを作りました。どうってことはないのですが、とりあえず、何でもIEnumerableなら.ConsoleWriteLine()すると、標準出力するやつです。便利です。
public static class TestExtend
{
public static IEnumerable<T> ConsoleWriteLine<T>(this IEnumerable<T> list, Func<T, string> func)
{
foreach (var item in list)
{
System.Console.WriteLine(func(item));
}
return list;
}
public static string ConsoleWriteLine(this string text)
{
System.Console.WriteLine(text);
return text;
}
public static IEnumerable<T> ConsoleWriteLine<T>(this IEnumerable<T> list)
{
foreach (var item in list)
{
System.Console.WriteLine(item.ToString());
}
return list;
}
public static IEnumerable<T> FileWriteLine<T>(this IEnumerable<T> list, string fileName, Func<T, string> func)
{
using (var file = System.IO.File.CreateText(fileName))
{
foreach (var item in list)
{
try
{
file.Encoding.GetBytes(func(item));
file.WriteLine(func(item));
}
catch { }
}
}
return list;
}
}
追記
https://gist.github.com/kiichi54321/77fb0816b841a338221a11a9bb047d00
拡張メソッドを、ここで管理するようにした。
最後のファイルに書き出すものは、思いっきりエラーを握りつぶしているので、嫌いな人はテキトウに変えてください。
他には、stringをintに変えるなどの型の変換を行う拡張メソッドも便利。スクリプト言語っぽく型の変換が簡単になる。
#まとめ
短いプログラム(スクリプト)の実行環境としてTestProjectは素晴らしい。
TestProjectは、メソッド単位で実行でき、期待通りの結果かどうかのチェックや標準出力を得ることができる。
Visual Studioからメソッドの実行ができるため、スクリプト実行環境になりうる。
(僕は昔、似たようなことをするためボタンだけのアプリを作って実行していました。それと比べたら数段クール)
メソッド単位の実行のため、一つのファイルに複数のスクリプトが入るので、管理しやすい。
共通部分は共通部分でつくることができるので、使い回しが楽。
当然、開発には、IntelliSenseが使え、Visual Studioのデバッグ環境が使える。
C#は、データ処理のLINQと ParaleForeach,Taskなど並列化ライブラリが標準装備なので、データ処理に向いている。
記述として長くなりがちなものも、拡張メソッドを使うと劇的にシンプルになるので、使う頻度が高いものは活用するのがオススメ。
わかっている欠点としては、Visual Studio上でしか実行できず、コマンドライン実行ができないことと、実行する値をその時に入力することができないので、ソースコードをいじることでしか実行時の値を操作できないこと。やや困る。