.net column
.NET開発者のためのブログメディア

プログラムコード

C#のref・out・inについてわかりやすく解説!ref・out・inの違いと制約

2021年07月08日
SE
C#のref・out・inは何が違うのですか。
PM
呼び出す側での初期化やメソッドでの値の代入の点で違いがあります。

C#のref・out・inとは?


C#にはrefやoutやinというキーワードがありますが、初心者の方はよく知らない人も多いでしょう。この記事はref・out・inについて分かりやすく解説するので是非ご覧ください。

C#でメソッドに値を渡して、その値を操作して結果を取得する場合、以下のように行うのが普通です。

refでフィールドをメソッドに渡すことができる

上の結果は7になります。これを「値渡し」と言います。基本的にこのやり方でプログラミングを行っていて問題はありません。ただ、フィールドをメソッドに渡してそちらで値を操作したい時もあるかもしれません。その場合はどうしたらよいのでしょうか。

それを実現するのがrefです。以下のC#サンプルのように使用します。

refで参照渡しが可能になる

上のC#サンプルを実行すると結果は7になります。前のサンプルと変わった所は、AddValueメソッドに3つ目のパラメータの「ref int c」が追加され、戻り値をreturnで返さずそのcにaとbを加算した結果を代入するようになっています。

AddValueを呼び出す側では、加算した結果を入れる変数のsumをrefキーワードを付けてAddValueに渡しています。これでAddValue側でsumに値が代入されます。この渡し方を「参照渡し」と言います。

フィールドを初期化せずに参照渡しするには?

上のC#サンプルではまずsumを0で初期化していますが、その後にAddValueで値が更新されるので無意味に感じます。しかしsumの宣言を「int sum;」とすると、「ref sum」の箇所で未割り当てのローカル変数が使用されましたというエラーになります。

初期化しないで参照渡しをする方法はないのでしょうか?それを可能にするのが、以下のC#サンプルで使用しているoutです。

outで初期化をせず参照渡しができる

上のC#サンプルは今までのサンプルのinをoutに変更して、sumの初期化を無くしただけです。これで今までと同じように動作します。outを使えばrefと違って渡すフィールドの初期化が不要になるということですね。

ただしoutにも制約があります。渡す側の初期化が不要になる代わりに、渡されたメソッドでの初期化が必須になります。以下のようにAddValueでcに値の代入をしていない場合エラーになります。refでは渡す側で初期化していればAddValueが空でもエラーにはなりません。

3つめのinの役割は?

C#にはrefとout以外にinと言うキーワードもあります。これはC# 7.2から追加されましたが、最新環境ではそのまま利用できます。以下がinを利用したC#サンプルです。

inを使えば読み取り専用にできる

上のC#サンプルではsumにinを付与してDispValueメソッドに渡し、DispValueではそれを表示しています。inを付与すると渡されたメソッドの先で値を変更できなくなります。例えば以下のようにするとエラーになります。

inを付与すると読み取り専用になるということです。フィールドの値を渡したメソッド内で変更されたくない時に利用すれば、間違いを防ぐことができますね。

ref・out・inの違いのまとめ

ref・out・inについてまとめると以下のようになります。この3つを正しく使い分けることで、間違って値が更新されることが無いようにしましょう。

ref
呼び出す側での初期化=必須
メソッドでの値の代入=してもしなくても良い

out
呼び出す側での初期化=禁止
メソッドでの値の代入=必須

in
呼び出す側での初期化=必須
メソッドでの値の代入=禁止

ref・in・outの制約

ref・in・outには制約があります。非同期のasyncのメソッドと、yield returnを使うメソッドにはref・in・outは使えません。理由はその2つのメソッドは呼び出す時点で結果が不定であるからです。例えば以下のC#サンプルはエラーになります。

yield returnは行き来が発生するため使用不可

上のC#サンプルのyield returnは通常のreturnと違いそこでメソッドが終わらずに、値を返しながら最後まで進みます。

呼び出し側とメソッドでの行き来が発生するため、最初の呼び出しで結果が確定しないという理由でref・out・inを使用できません。なお上のC#サンプルの2か所のinを除去すると正しく動作し、以下のような結果になります。

4
5
6

ref・out・inの効果的な利用法

ここまで読んできてref・out・inにあまり使い道が無いように感じた人もいるかもしれません。しかしref・out・inは構造体(struct)を使う時に効果を発揮するのです。以下のC#サンプルをご覧ください。

ref・out・inを使えば構造体の処理で高速化できる

上のC#サンプルは構造体をinで渡しています。C#ではクラスを渡す場合は参照渡し、構造体を渡す時は値渡しになります。しかしもし構造体のサイズが大きいと、メソッドに値を渡す時に値渡しのため中身全てを渡すことになって、非常にパフォーマンスが低下します。

しかしref・out・inを使えば構造体の参照渡しができます。渡すのはアドレスだけになるためパフォーマンスが良くなるのです。なお上のサンプルの結果は以下になります。

あいうえお
かきくけこ
さしすせそ

SE
C#のref・out・inのそれぞれの違いや制約についてよく分かりました。
PM
C#のref・out・inはあるケースでは必須の機能ですので、それぞれの違いをしっかり理解して、活用してください。

C#のref・out・inを活用しよう

C#のref・out・inについて活用しましたがご理解頂けましたでしょうか。構造体を利用する時にパフォーマンス改善のために必要になるので、是非活用してください。


.NET分野でのキャリアアップをお考えの方は、現在募集中の求人情報をご覧ください。

また、直接のエントリーも受け付けております。

エントリー(応募フォーム)

Search

Popular

reccomended

Categories

Tags