メソッド -参照渡し-

ホームC#プログラミング応用講義 > 参照渡し

目次

参照渡しとは

C# において始めに躓くのが「値型と参照型」であると述べたように思います。変数だけを理解するなら「参照はアドレスが入ってるだけ」ということを覚えておけ ば説明できますが、メソッドの引数の中に参照が絡んでくると難しくなってきます。

Reference
もう1度おさらいしますが、参照とは実態へのアドレスのことでした。
参照型の変数は存在する実体を持っておらず、メモリ上に存在するオブジェクトなどの実体へのアドレスを保持します。
ここまでは復習です。前回は参照に「目」をつけたりはしてなかったと思いますが、目があったほうがわかりやすいでしょう?

この参照の機能を利用すれば、メソッドで値を2つ返すのと同じような機能をスマートに実装できます。
メソッドは1つしか値を返すことができませんから、複数の値を呼び出し元からも利用したい場合は参照を利用すると便利です。

ここで参照を利用するのは引数においてです。値を渡すときに参照型にして渡し、受け取るときも参照型で受け取れば「アドレス」が同じ実体を参照することに なります。
この、参照型を利用して値を渡し、メソッド定義の部分でも値を参照として受け取るメソッドの呼び出し方法を「参 照渡し」といいます。

「参照渡し」というのは特に値型の変数を参照化することを言います。
int 型は値型ですが、これを参照型にして利用するのが「参照渡し」です。

例えば、a,b の値をひっくり返すメソッドを実装するにはこの「参照渡し」が不可欠です。値が2つあるからというのはお分かりでしょう。

参照渡しには
あらかじめ値を代入してある変数を参照型にして「参照渡し」
何も入ってない変数を参照型にして「参照渡し」
という2つがあります。それぞれについて、下で説明します。

ちなみに、参照渡しは英語で「pass-by-reference (参照によって渡す)」といい、値渡し(普通の渡し方)を「pass-by-value (値によって渡す)」といいます。
Reference の頭文字2つは下で説明するキーワードになっていますから、関連付けて覚えるといいでしょう。

ref

参照渡しに関する全般概要は説明しましたので、ソースを見てみましょう。
using System;

class Sample
{
public static void Main(string[] args)
{
RefClass refclass = new RefClass();

int a = 10;
int b = 20;

Console.WriteLine("a=" + a);
Console.WriteLine("b=" + b);

refclass.ReferenceMethod(ref a,ref b);

Console.WriteLine("a=" + a);
Console.WriteLine("b=" + b);
}
}

class RefClass
{
public void ReferenceMethod(ref int x,ref int y)
{
int t;
t = x;
x = y;
y = t;
}
}
あらかじめ答えを書いておきましょう。このプログラムは
a=10
b=20
a=10
b=20
と表示される、2つの値を入れ替えるプログラムです。

まずは
RefClass.ReferenceMethod()
を見ましょう。

このメソッドの中で施される処理は以前にも「ソート」において説明済みです。変数 t を仲介にして、
・x の値を t にバックアップ
・x はバックアップがあるから x に y の値を代入
・y の値はすでに x に代入されているから x のバックアップ t を y に代入
というアルゴリズムです。

このメソッドで x と y がひっくり返っていますが、値は返していません。それでも呼び出し元に制御が戻ったときはきちんとひっくり返っています。
ここで、先に述べたメソッドの定義部分を見て見ましょう。

public void ReferenceMethod(ref int x,ref int y)
ref
このキーワードが見つかります。このキーワードこそが、「値を参照で受け取るよ」を意味するものです。

次にメソッドの呼び出し部分を見ます。
refclass.ReferenceMethod(ref a,ref b);
ここでも
ref
が用いられています。このキーワードは1つだけで利用することはなく、定義部分と呼び出し部分の2箇所で 利用することになります。

ここで重要なのは定義部分の仮パラーメータに代入される値です。もし ref キーワードを利用しなければ
x = 10;
y = 20;
がそれぞれ代入されますが、ここでは参照であるため、
x = a;
y = b;
の参照が入り、a と x、b と y はそれぞれ同じ実体 10, 20 を参照することになります。

そのため、メソッド内で x, y をいじれば a, b もいじられることになります。この過程は図で表すのが難しくて、結局かけませんでしたが、参照型の特徴を把握して理解してください。わかれば簡単です。

この ref を用いた参照渡しで利用するパラメータは特に「参照パラメータ」 と呼ばれます。

out

ref キーワードは必ず渡す変数が初期化されていなければなりません。すなわち、何らかの値が入っていなければなりません。
その値を Reference(参照) として渡すのが ref キーワードなので、何も入っていないというのはあってはなりません。

しかし、何も無理やり値を入れなくてもいいときがあります。むしろ、メソッドの中からはじめて初期化するのがいい場合があります。
その際は ref の代わりに以下のキーワードを利用します。

out
このキーワードを利用することで、初期化されていない変数でも参照渡しできます。
using System;

class Sample
{
public static void Main(string[] args)
{
RefClass refclass = new RefClass(5,25);

bool b;
refclass.ReferenceMethod(out b);

Console.WriteLine(b);
}
}

class RefClass
{
int x,y;

public RefClass(int x,int y)
{
this.x = x;
this.y = y;
}

public void ReferenceMethod(out bool tf)
{
if (y == x * x) tf = true;
else tf = false;
}
}
久しぶりに関数が出てきました。

y = x2
この関数に対してコンストラクタで、x, yの値を代入し、(左辺) = (右辺) が成立すれば true 、成立しなければ false を表示します。

bool b;
refclass.ReferenceMethod(out b);
この部分で bool 型の変数 b には何も入っていません。何もといっても b に入りうるのは「true or false」 ですが、どっちみち初期化されていません。
public void ReferenceMethod(out bool tf)
受け取る部分でも
tf = b;
となるため、tf は何も参照していません。ただし、ここでは何も入っていないのではなく、何も参照してい ないのであって、参照渡しになっています。

if (y == x * x)
初歩的ですが、等価演算子(==)になっていることを確認してください。代 入演算子(=)ではありません。この条件式は bool 型の true / false が返されるので、if が利用できます。

int x,y;

public RefClass(int x,int y)
{
this.x = x;
this.y = y;
}
これも初歩的です。ファイル変数とローカル変数が同じ名前です。そのため
this
キーワードを用いてファイル変数にアクセスし、区別しています。

this はあってもなくても変わらない無用なものという認識があっては、このように利用する際に困ります。this は自分自身のオブジェクトを参照する重要なキーワードですから確認しましょう。

Console.WriteLine(b);
なぜこれで真偽値が表示されるのか不思議かもしれません。これが参照型の特徴です。
メソッド内で tf をいじることで、同じ実体を参照する b も、呼び出したときとは変わっています。

また、bool 型を WriteLine() に渡したら
True / False
のどちらかが、始めが大文字になって文字列で表示されることも確認しましょう。

私も参照型については入門段階ではかなり逃げていた記憶があります。しかし、いつか壁にぶつかってしまいます。
参照がわからなければ、このページに乗せたサンプルもサッパリだと思います。

わからなければじっくり時間をかけてください。このページを読み飛ばしては後に損をすることになります。

[ ステップアップC# ] [ C#プログラミング応用講義 ]