タイトルは正確ではありません。すいません。if式っぽく書ける条件判定関数的なものです。if式っぽく書きたかったので作ってみました。
C#のif
は文であって式ではありません。式ではないので
var name = "ほげほげ君";
// こうは書けない
var exName1 = if (name.EndsWith("君")) {
return name;
}
else {
return name + "君";
}
// こう書く
var exName2 = "";
if (name.EndsWith("君"))
exName = name;
else
exName2 = name + "君";
みたいに書かなければいけません。人によるかと思いますが、これ読みにくくないですか?exName2
への代入が2箇所あるので読んでて鬱陶しいです。何度もこういうコードが出てくると嫌です。
このくらいのコードであれば三項演算子で解決するという方法もあるでしょう。
var name = "ほげほげ君";
var newName = name.EndsWith("君") ? name : name + "君";
これであれば代入は1箇所ですし分かりやすいですね。しかし、次のようなケースではどうでしょう。
// 三項演算子
var newName = name.EndsWith("さん") ? Regex.Replace(name, "さん$", "君") : (name.EndsWith("君") ? name : name + "君");
// if文
var newName = "";
if (name.EndsWith("さん"))
newName = Regex.Replace(name, "さん$", "君");
else if (name.EndsWith("君"))
newName = name;
else
newName = name + "君";
最後が"さん"であれば"君"に変換するという場合は、三項演算子を使えば分かりにくくなり、if
else if
else
では悪戯に長くなりますし結局代入箇所が分散していますね。もっとこう、スパっと書けないものでしょうか。ということでif式っぽいものを作りました。
// ex.1
var newName1 = Ext.If(name.EndsWith("さん"), Regex.Replace(name, "さん$", "君"))
.ElseIf(!name.EndsWith("君"), name + "君")
.Else(name);
あるいは
// ex.2
var newName2 = name.EndsWith("さん").Then(Regex.Replace(name, "さん$", "君"))
.ElseIf(!name.EndsWith("君"), name + "君")
.Else(name);
上記のように書くためのメソッドです。条件に対して欲しい結果をメソッドチェーンで書いていけるのでなんとなく読みやすいと思います。
まずはex.1にあるIf
ElseIf
Else
というメソッドがこちら。
public static Tuple<bool, T> If<T>(bool term, T value)
{
if (term)
{
// 条件が成立する場合はvalue
return new Tuple<bool, T>(true, value);
}
else
{
// 条件が成立しない場合はT型のdefault
return new Tuple<bool, T>(false, default);
}
}
public static Tuple<bool, T> ElseIf<T>(this Tuple<bool, T> prev, bool term, T value)
{
if (prev.Item1)
{
// 前の式が成立している場合はそれをそのまま返す
return prev;
}
else if (term)
{
// 条件が成立する場合はvalue
return new Tuple<bool, T>(true, value);
}
else
{
// 条件が成立しない場合はT型のdefault
return new Tuple<bool, T>(false, default);
}
}
public static T Else<T> Else(this Tuple<bool, T> prev, T value)
{
if (prev.Item1)
{
// 前の式が成立する場合は、その値を取り出して返す
return prev.Item2;
}
else
{
// 前の式が成立しない場合は規定値としてvalueを返す
return value;
}
}
if式を実現するにあたり、前の式の計算結果と値を受け取って引き継ぎつつ、最終的には値のみを返すという処理をする必要があります。そのため、Tuple<bool, T>
でそれらの情報を受け渡し、最終的にElse
でT
型の値部分のみを返しています。
ElseIf
とElse
ではTuple<bool, T>
の拡張メソッドを使用しています。なので、このメソッドはstatic
なクラスで宣言する必要があります。
続いてex.2にあるThen
について。ex.2のElseIf
とElse
はex.1のものと同様です。
public static Tuple<bool, T> Then<T>(this bool term, T value)
{
if (term)
{
// 条件が成立する場合はvalue
return new Tuple<bool, T>(true, value);
}
else
{
// 条件が成立しない場合はT型のdefault
return new Tuple<bool, T>(false, default);
}
}
これも単純にex.1のIf
の引数term
を、引数ではなくメソッドチェーン的に取れるようにthis
キーワードで拡張したメソッドになります。this
キーワードにしている引数は明示的に指定することも可能なので、なんならex.1のIf
メソッドをこのThen
メソッドで代用することも可能です。(条件を指定するならIf的な名前がいいと思って別に定義しました)
ElseIf
は必要に応じて省略することも、複数記述することもできます。
var point = GetPoint();
var score = Ext.If(point == 100, "S")
.ElseIf(point >= 80, "A")
.ElseIf(point >= 60, "B")
.ElseIf(point >= 40, "C")
.ElseIf(point >= 30, "D")
.Else("E");
このように書けます。if文でちまちま書いたり三項演算子でネストして書いていくよりはるかにわかりやすいと思います。
Func
やAction
を受け取るよう拡張すれば、ラムダ式などを使ってより柔軟な表現が出来るようになると思います。たとえばActionだと、
public static bool Then(this bool term, Action action)
{
if (!term)
return false;
action();
return true;
}
public static bool ElseIf(this bool previous, bool term, Action action)
{
if (previous)
return true;
else if (!term)
return false;
action();
return true;
}
public static void Else(this bool previous, Action action)
{
if (previous)
return;
action();
}
// ***
var point = GetPoint();
(point == 100).Then(() => Console.WriteLine("S"))
.ElseIf((point >= 80), () => Console.WriteLine("A"))
.ElseIf((point >= 60), () => Console.WriteLine("B"))
.ElseIf((point >= 40), () => Console.WriteLine("C"))
.ElseIf((point >= 30), () => Console.WriteLine("D"))
.Else(() => Console.WriteLine("E"));
こんな感じで書けます。
ジェネリクスを使用していますが制約などはつけていませんし、より最適なコードもあるかもしれません。ある程度拡張性はあると思いますが、もっといいやり方あるよ!などがあれば、是非教えてください。
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント