C# 一つのメソッドから複数の値を取得する3つの方法
あいさつ
どうも、はかせです。
唐突ですが、一つのメソッドから複数の結果を欲しくなる時ありませんか?
「座標情報と対応インデックスが欲しい」
「メソッド実行して値とそのメソッドの成否が知りたい」
ゲーム開発でもライブラリ開発でもそういった場面に遭遇することは少なくないかと思います。
ということで今回は私が実際に使ったことがある
一つのメソッドから複数の値を取得する方法を
どういう時につかったとかの私の所見も踏まえて
3つご紹介します。
refキーワードを使う
C#ではrefキーワードを使うことで参照渡しを実現できます。
参照渡しとは平たく言えばそのメソッド内で参照渡しで渡された引数を
変更したらその変更がそのまま呼び出し元まで伝わるって感じです。
この機能を使って引数に複数の値を設定することで
実現します。
ではコードです。
private static bool IsGetData(ref int Num,ref string Name) { bool isSuccesed = false; Num = 1; Name = "Hakase"; isSuccesed = true; return isSuccesed; }
戻り値にboolを設定しているのはメソッドの成否を返したいからですね。
別に必要と言うわけではありませんが、
あると便利です。
そして呼び出し方はこう
int num = 0; string name = ""; var result = IsGetData(ref num,ref name);
参照渡しの場合引数は事前に定義されている必要があります。
無い変数の参照は渡せませんからね。
実行するとnumには1が、
nameにはHakaseがそれぞれ格納され、
resultにはtrueが入ります。
私がこの形を使うのは
・初期化処理
・解放処理
・I/O処理
この3つですね。
初期化も解放もちゃんと正常に実行されたか知りたいですし、
I/O処理も完了や成功を欲しくなります。
なので成否を戻り値に、
その他何か返したいものがあった場合は参照渡し使ってやります。
今回は例なのでintとか使ってますが、
実際使うときは受け渡し用の構造体とか作ってやります。
そっちの方が値の種類を増減させたいとき楽なので。
タプルを使う
C#7.0以降で使える機能です。
匿名型なんて言われますね。
その通りで名前のない型を作る機能です。
(私はそう認識しています)
匿名型も型ですから
複数のメンバを持てます。
要はそこにintやらstringやらを設定して
その型を返すってわけです。
ではコード
private static (int num,string name) GetTupleData() { (int, string) data; data.Item1 = 1; data.Item2 = "Hakase"; return data; }
ラムダっぽくもあり引数チックでもあるって書き味ですね。
個人的には好きです。
受け手はこんな感じ
var result = GetTupleData();
//普通にpublic変数触るみたいに触れる
var number = result.num;
var name = result.name;
varが使えるんで極めて楽に取れます。
変数取得も普通のクラスと変わりませんね。
私がタプルを使う場面は
・複数の値が欲しいが他の場面では使わない組み合わせの場合
今のとここれだけですね。
他に使うべき場面があれば教えてください。
さてどういうことかって言うと
例えば
「敵の座標と番号を調べて配列に格納したい」
こんなケースがあったとします。(というかありました)
番号も座標も別ファイルであって、
それを読み込んで対応付けます。
(RDBの主キー外部キー的なノリ)
そうなった時わざわざ座標と番号をセットにした
構造体なりなんなりを作るかって話です。
一回配列に放ってしまえば配列インデックスで事足りますからね。
つまりそういう場面のときにタプル使ってやりました。
私としてはまだ限定的な場面でしか使わないんじゃないかなと思っております。
(タプルナンモワカラン)
構造体を使う
構造体でもクラスでもなんでもいいんですが、
要は複数の値がセットされた型を定義してそれを返すやり方です。
つまりこんな感じ
//まとめた構造体 public struct MyData { public int Number; public string Name; } //まとめた構造体に値セットして返す private static MyData GetData() { MyData data; data.Number = 1; data.Name = "Hakase"; return data; }
極めてスタンダートでわかりやすいですね。
シンプルイズベスト。
私がこれを使うのは
・長期にわたって管理するデータの組み合わせを取得or作成する
・成否情報が無くても困らない
・その他フレームワーク等に乗っかるとき
こんなとこです。
例えばオブジェクトプール的な感じで
最初にデータをガっと作って使いまわすときとかは
明確に型作ってやり取りする方が楽なんでそうしてます。
(ただDOTSの登場でオブジェクトプールもオワコン気味になってる気がしなくもない)
失敗してたらnullでもぶち込んでおけばいいやってときもこうですね。
初期化とかは失敗したときの
エラーハンドリングしたいんで成否欲しいですが、
そうではないときはnulチェックでもかけて
弾いてやればいいだけですし。(適当)
あとはなんか外部のフレームワークとか使うときに
明確に型が決められてる時とかもこれですね。
というかその時はもはやその型のインスタンスを作るファクトリメソッドと化しますが。