著作一覧 |
rubyではreturnに複数の値を,
で列挙して関数から一度に複数の値を返すことができる。
def mfun(x, y) return x + 1, y + 1 end a, b = mfun(3, 4) puts "#{a}, #{b}" #=> 4, 5
同じようなことをC#でやる場合には
のいずれかが必要である(C#4.0未満)。
1は下品だし、2は型が異常だし、3は宣言が面倒だ。
特に1が最悪なのは、返す値(というよりも種類)を増やそうとするとメソッドシグネチャが変更されてしまうことだ。それに比べれば2は柔軟、3は安全なので悪くはない。
ということは、値を返す場合は、戻り値を利用するという原則はやはり原則として正しいということだ。
ただ、2は異常だ(工夫しどころとしてはインデックスをEnum名として名前と値を定義づけるという方法はあるが、そんなことをするのであれば素直に3とすべきだ)。
問題は3は面倒だということで、3の面倒さを解消するのが方法論としては正しい。
というわけで、C#4.0であれば、型としてdynamicを利用できるのでそれを利用する。
class MVal { static dynamic mfunc(int x, int y) { return new { X = x + 1, Y = y + 1 }; } static void Main() { var v = mfunc(3, 4); System.Console.WriteLine(v.X + ", " + v.Y); // => 4, 5 } }
追記:mayukiさんのご指摘の通りTupleを使う方法もあって、コードサイズ(dynamicはすごいものに展開される)とおそらく実行効率(dynamic処理が不要だし、ボクシングも不要ではなかろうか)は次の方法が一番良い。のだけど、プロパティ名のかっこ悪さと意味の無さが、コード可読性についてはobject配列とほとんど同じ(匿名クラスであればプロパティに意味のある名前を使える)で、それがすごく気に食わない(ので僕の中では完全に無かったもの扱いになっていた)。が型付けされている分、object配列よりも良いことはもちろん良い。というわけで下はTupleの例。
using System; class MVal { static Tuple<int, int> mfunc(int x, int y) { return Tuple.Create(x + 1, y + 1); } static void Main() { var t = mfunc(3, 4); System.Console.WriteLine(t.Item1 + ", " + t.Item2); // => 4, 5 } }
さらに追記:確認したらTupleのItemXプロパティは構造体は構造体でボクシングされていなかった。
IL_001a: callvirt instance !1 class [mscorlib]System.Tuple`2<int32,int32>::get_Item2() <-- スタックトップにint32のItem2の値が乗る。 IL_001f: box [mscorlib]System.Int32 <-- Console.WriteLineの引数にするためにスタックトップをボクシングする。
ジェズイットを見習え |
利用箇所が局所的であればTuple.CreateでTupleを作って返すので事足りそうですがどうでしょうか?
もちろん、全然OKなんですが、TupleってItem1とかItem2とかいうプロパティ名が最悪ではないですか? こんなの使うくらいだったら無名クラスで1文字プロパティにしたほうが楽じゃないですか(でもnewするよりもTuple.Createのほうが楽かな? でも戻り値の型宣言が面倒な気がするけどと思ったけど試したら別にdynamicでも良いのか(でも気分的には無名クラスと異なり型を明示してTuplue<int, int>とか宣言すべきな気がするけど))。