前回の記事の続きです。
http://qiita.com/higty/items/ac19b15e2967901982a8
この記事では
・型スイッチ(定数、型、変数とのマッチング)
・Out Variables
・ローカル関数
を解説します。
型スイッチ
IoTでデバイスのデータを保存しているとします。例えば以下のようなデータです。温度(Temperature)、Frequency(3軸振動数)、Gps(位置情報)を記録しているとします。
public class Temperature
{
public double Value { get; set; }
}
public class Frequency
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
public class Gps
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
以下のようにデータをObject[]として取得できるとします。それをフィードとしてHTMLで表示するアプリケーションをイメージしてください。
public class IoTDataRepository
{
public static Object[] GetData()
{
//ほんとはAzureIoTHubとかからJSONでデータを取得しParseする感じかな?
var list = new List<Object>();
list.Add(new Temperature(22.4));
list.Add(new Frequency(422.33, 396.21, 522.12));
list.Add(new Gps(35.684502, 139.784171));
list.Add(new Temperature(18.4));
return list.ToArray();
}
}
HTMLの構築部分ですが今までのC#だとどうしてもコードが煩雑になりがちです。だいたいこんな感じになります。(本当はテンプレートエンジンとか使うと思いますが例なので文字列で構築)
foreach (var item in IoTDataRepository.GetData())
{
if (item is Temperature)
{
var t = item as Temperature;
var html = $"<tr><td>温度</td><td>{t.Value}</td></tr>";
}
if (item is Frequency)
{
var f = item as Frequency;
var html = $"<tr><td>振動数</td><td>X:{f.X}<br />Y:{f.Y}<br />Z:{f.Z}<br /></td></tr>";
}
if (item is Gps)
{
var g = item as Gps;
var html = $"<tr><td>温度</td><td>何か地図を表示するよ。</td></tr>";
}
//HTMLの構築処理とか
}
型スイッチを使用すると
foreach (var item in IoTDataRepository.GetData())
{
if (item is Temperature t)
{
var html = $"<tr><td>温度</td><td>{t.Value}</td></tr>";
}
if (item is Frequency f)
{
var html = $"<tr><td>振動数</td><td>X:{f.X}<br />Y:{f.Y}<br />Z:{f.Z}<br /></td></tr>";
}
if (item is Gps g)
{
var html = $"<tr><td>温度</td><td>何か地図を表示するHTML</td></tr>";
}
//HTMLの構築処理とか
}
かっこを無くせば
var html = "";
foreach (var item in IoTDataRepository.GetData())
{
if (item is Temperature t) html = $"<tr><td>温度</td><td>{t.Value}</td></tr>";
if (item is Frequency f) html = $"<tr><td>振動数</td><td>X:{f.X}<br />Y:{f.Y}<br />Z:{f.Z}<br /></td></tr>";
if (item is Gps g) html = $"<tr><td>温度</td><td>何か地図を表示するHTML</td></tr>";
//HTMLの構築処理とか
}
という感じでだいぶすっきりします。
型スイッチはswitch文にも導入されます。今までのC#だとcase文の部分が煩雑になりがちです。
foreach (var item in IoTDataRepository.GetData())
{
switch (item.GetType().Name)//switchの条件分岐の値は定数しか使えない
{
case "Temperature";
{
var t = item as Temperature;
var html = $"<tr><td>温度</td><td>{t.Value}</td></tr>";
break;
}
case "Frequency";
{
var f = item as Frequency;
var html = $"<tr><td>振動数</td><td>X:{f.X}<br />Y:{f.Y}<br />Z:{f.Z}<br /></td></tr>";
break;
}
case "Gps";
{
var g = item as Gps;
var html = $"<tr><td>温度</td><td>何か地図を表示するHTML</td></tr>";
break;
}
default: throw new InvalidOperationException("Missing case condition.");
}
//HTMLの構築処理とか
}
C#7.0では
var html = "";
foreach (var item in IoTDataRepository.GetData())
{
switch (item)
{
case Temperature t; html = $"<tr><td>温度</td><td>{t.Value}</td></tr>"; break;
case Frequency f; html = $"<tr><td>振動数</td><td>X:{f.X}<br />Y:{f.Y}<br />Z:{f.Z}<br /></td></tr>"; break;
case Gps g; html = $"<tr><td>温度</td><td>何か地図を表示するよ。</td></tr>"; break;
default: throw new InvalidOperationException();
}
//HTMLの構築処理とか
}
とても書きやすくなります。
型スイッチにはwhenというキーワードも導入されます。温度が30℃以上の場合は文字の色を赤にしたいという要件があったとします。こういう場合にはwhenを使用すると簡単に書けるようになっています。
foreach (var item in IoTDataRepository.GetDataFromJson())
{
switch (item)
{
case Temperature t when t.Value > 30; var html = $"<tr><td>温度</td><td style=\"color:#ff0000;\">{t.Value}</td></tr>";
case Temperature t; var html = $"<tr><td>温度</td><td>{t.Value}</td></tr>";
case Frequency f; var html = $"<tr><td>振動数</td><td>X:{f.X}<br />Y:{f.Y}<br />Z:{f.Z}<br /></td></tr>";
case Gps g; var html = $"<tr><td>温度</td><td>何か地図を表示するよ。</td></tr>";
default: throw new InvalidOperationException();
}
//HTMLの構築処理とか
}
使いどころはメタ的なプログラムをする部分や横断的なコードを記述する部分などでしょう。
・DBの型情報から自動生成するマスタメンテのコントロールの作成部分とか
・ASP.NETのDIで引数で渡されるControllerクラスを各コントローラーごとで処理を変えたいとか
・モバイルの通知などで新着予定、新着タスク、新着メッセージなどをそれぞれの形式で表示したいとか
型スイッチという名前通りObject型に様々な型が入っていてそれぞれの型ごとに処理をしたいような場合に活用ができると思います。
Out引数の改良
これはちょっとした改良ですがout引数用の変数を宣言する必要がなくなり簡単に書けるようになりました。
var x = 0;
if (Int32.TryParse("123", out x) == true)
{
Console.Write(x.ToString());
}
と書いていたのが
if (Int32.TryParse("123", out var x) == true)
{
Console.Write(x.ToString());
}
というように簡単に書けるようになります。楽ちん。
ローカル関数
今までのC#でフィボナッチ数列を書くには
static void Main()
{
Console.WriteLine(F(0));
Console.WriteLine(F(1));
Console.WriteLine(F(4));
Console.ReadLine();
}
private static int F(int value)
{
return value <= 1 ? value : value * F(value - 1);
}
と書いていました。ラムダ式を使って
static void Main()
{
Func<int, int> F = value => value <= 1 ? value : value * F(value - 1);
Console.WriteLine(F(0));
Console.WriteLine(F(1));
Console.WriteLine(F(4));
Console.ReadLine();
}
と書きたいところですがコンパイルエラーになります。Fの宣言の中でFを呼び出すことができないためです。ローカル関数を使用するとこういった再起処理などで上記のような書き方ができます。
static void Main()
{
F(int value)
{
return value <= 1 ? value : value * F(value - 1);
}
Console.WriteLine(F(0));
Console.WriteLine(F(1));
Console.WriteLine(F(4));
Console.ReadLine();
}
もちろんラムダ式として書くこともできます。
static void Main()
{
F(int value) => value <= 1 ? value : value * F(value - 1);
Console.WriteLine(F(0));
Console.WriteLine(F(1));
Console.WriteLine(F(4));
Console.ReadLine();
}
匿名メソッドで変数がキャプチャされる場合に比べ、パフォーマンスが良くなる場合があります。
C#7.0が使えるようになるのが楽しみです。