i-school - ref キーワードを活用した参照戻り値の実装例

ref キーワード


 ref キーワードは、変数を参照して利用できるようにするための機能です。

MicroSoft
ref (C# リファレンス)



++C++; // 未確認飛行 C 様
参照渡し


 ref キーワード自体には複数(現在 5 種類)の利用方法が用意されていますが、ここでは【参照戻り値】と【ref ローカル変数】についての説明と実装例を提示します。


参照戻り値と ref (参照)ローカル変数


 メソッドの戻り値の型の前に ref キーワードを利用することで、戻り値の値に対して参照渡しを行うことが出来ます。

<参照戻り値のあるメソッド>
  private ref int SampleMethodRef(string name) { }



 利用する場合には、戻り値の型の前、値を渡す側、値を受ける側に、それぞれ ref 修飾子を記述します

 ローカル変数についても ref キーワードを利用することで、代入する変数の値を参照渡しすることが出来ます。

 その場合、参照として引数を渡す側、そして参照を受け取る側の両側に ref キーワードが必要になります。

<参照戻り値のあるメソッドを利用して ref ローカル変数に参照値を受け取る>
  ref int count = ref SampleMethodRef("itemName");  

参考サイト
++C++; // 未確認飛行 C 様
参照戻り値と参照ローカル変数



実装例


 上記の説明を元に、実際に利用する例を記載します。
なお、比較できるように、参照戻り値を利用するケースと利用しないケースを提示しています。

 複数の種類のアイテムが、それぞれ別の所持数として管理されている場合に、指定したアイテムの所持数の値を更新する処理に利用したケースです。

 通常の処理の場合、アイテムの種類ごとにメソッドを用意して、その中で所持数の更新を行うことが多いですが、
参照戻り値を利用することで、1つの変数に、そのときに更新が必要なアイテムの所持数を参照して取得することにより、
複数のメソッドを用意することなく、また、追加のプログラムの必要なく、プログラム内の所持数の更新部分を一元化することが出来ています。


ItemName .cs

<= クリックすると開きます。



<参照戻り値を利用しないケース>


PlayerHealth.cs

<= クリックすると開きます。


 この実装の場合、アイテムの種類が増えるごとに、似たような処理を switch 文内に逐次追加していく必要があります。
そのため、アイテムの種類を増やすたびに、アイテムの所持数を更新する処理自体が追加されることになり、煩雑かつ、手間が増えることになります。


<参照戻り値と参照ローカル変数を利用するケース>


 参照戻り値と参照ローカル変数を利用することで、利用しない場合には1つのメソッド内で実装していた処理を2つのメソッドを分けて作成しています。

 参照している値の更新を行う UpdateRecoveryItemCount メソッドと、どの値を参照するかを決定する GetRecoveryItemCountRef メソッドに分けているので、
修正する場合には、GetRecoveryItemCountRef メソッドのみ修正する形式になっています。

 今回の場合、参照戻り値の機能を利用することにより、戻り値の値をメンバ変数の参照値とすることができますので、
その値を増減した場合には、メンバ変数の値そのものが変更されることになります。


PlayerHealth.cs


 こちらのケースの場合、UpdateRecoveryItemCount メソッド内で参照した値の更新を行う機能が備わっているので、
今後、アイテムの種類が増減しても、このメソッド内への修正は必要ありません。

 代わりに、GetRecoveryItemCountRef メソッド内の switch 文の分岐のみ修正すれば済みます。

 private int x = 0; の宣言は、GetRecoveryItemCountRef メソッド内の switch 文の default で利用するために宣言しています。
理由は、参照戻り値として指定できる変数にはローカル変数は利用できないというルールがあるためです。
例えば、GetRecoveryItemCountRef メソッド内で int x = 0; を宣言した場合、x は上記のルールにより、指定するとエラーになります。

 なお戻り値のある switch 文の場合には式での書式の switch 文が利用できますが、参照戻り値の場合には、式での書式は利用できません
(通常の switch 文で記述する必要があります)



 参照利用しているポイントは、以下の部分に効果的に利用されています。

UpdateRecoveryItemCount メソッド内
  // 回復アイテムの所持数を、最大値と最小値に収まるように制限して更新する
  recoveryItemCount = Mathf.Clamp(recoveryItemCount + updateValue, 0, maxCount);  // ☆ 参照値を更新するので、メンバ変数自体が変わる

 この recoveryItemCount の値が、その前の処理で参照して取得した、いずれかのメンバ変数の値になっています。
メンバ変数の値のコピー値ではないので、この値を変更すると、メンバ変数の値が参照されて変化します

 そのため、アイテムの所持数を更新する処理はこの1つだけですが、更新を行う値がその都度、参照先を変えるようなロジックになっているので
いずれのメンバ変数の値であっても対応出来るようになっています。


参照戻り値と参照ローカル変数を利用した場合のデバッグ用ソースコード


 上記の参照戻り値と参照ローカル変数を利用した場合のデバッグ用のソースコードを掲載しておきます。
作成したら、任意のゲームオブジェクトにアタッチしてゲームを実行してデバッグを行います。

 Start メソッド内で実行する UpdateRecoveryItemCount メソッドの指定を変えて、処理の内容を確認をします。
このソースコードでは減算のみしていますが、当然加算も出来ますので、自分で処理を書き換えて確認してみてください。

 このデバッグにおいて大切なのは、参照ローカル変数については、参照戻り値としてメンバ変数が参照されているかを確認します。
そして参照ローカル変数を更新することで、参照されているメンバ変数の値が正常に更新されていることを確認することです。

 デバッグする意味とデバッグの内容を理解した上で確認を行うことが大切になります。


PlayerHealth.cs

<= クリックすると開きます。


 出力結果です。

Console ビュー



 インスペクター内も、Debug モードに切り替えることで、メンバ変数が参照されて値が変化していることを確認できます。


インスペクター(Debug モード) 初期値


     ↓

インスペクター(Debug モード) デバッグ実行時



まとめ


 すべての処理に応用できるわけではありませんが、戻り値の利用方法の1つとして押さえておくとよい機能になります。
タプルを利用した戻り値もそうですが、スキルの引き出しを増やす(今回であれば、色々な戻り値の使い方を知っている)ことで視野の広い設計が可能になります。

 以上になります。