今回の記事ではメソッド(関数)についてみていきます。
以前の記事でもメソッド自体を既に使っています。今回の記事を読むことで色々な種類のメソッドを作ることができますので、是非呼んでみてください。
前回の記事:
メソッド(関数)とは
メソッドとはスクリプトの処理をまとめたもので、アプリケーションはたくさんのメソッドを組み合わせて構築されています。また、クラスに所属していないメソッドは関数とも呼ばれます。
機能や使い方は同じですがC#はオブジェクト指向言語なのでこの記事では以降、メソッドと呼びます。
メソッドを使用する際は先に定義し、それを呼び出す必要があります。
メソッドの呼び出し方
メソッドの呼び出しはこれまでの記事で何回か書いていますが、改めて紹介します。
C#ではメソッドは全てクラスのメンバになっていますので、メソッド呼び出しにはどのクラスのものか指定する必要があります。
メソッドの呼び出し方:<クラス名 or 変数名> . <メソッド名> (<引数>…)
メソッドには引数という入力値を渡すことができます。引数の数はメソッドによって異なりますが、必ずメソッドの定義にある個数分、値を渡してください。
また戻り値というメソッドの呼び出し結果を呼び出し元に出力するものがあります。戻り値は値と同じように使うことができ、他の変数に代入したり計算の中に組み込むことができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//サンプル用に簡単なクラスを定義している。 class A { public void Method() {} public int Method2(int a, int b) {return a+b;} } var a = new A(); //aのメンバであるMethod()を呼び出している。 a.Method(); //aのメンバであるMethod2を呼び出している。 // Method2は引数を2つ取り、int型の戻り値を返すメソッドになるので、 // 引数を1と2の2つ渡してあげている。 // また、戻り値をcの初期値として使っている。 var c = a.Method2(1, 2); //戻り値は以下のように値と同じように使うこともできる。 var d = 1 + a.Method2(3, 4); |
メソッドの書き方(定義)と構造
呼び出し方について説明しましたので、次はメソッドの定義の書き方について説明していきます。
メソッドはクラスの中だけに書くことができます。
メソッドの定義:<戻り値> <メソッド名> (<引数>, ...) { <メソッド内で行う処理> }
構造を大きく分けると次のようになります。
- 戻り値
- メソッド名
- 0個以上の引数
- メソッド内で行う処理
この中で特に戻り値と引数がメソッドの大きな要素になりますので、これらについて詳しく解説していきます。
戻り値について
戻り値には型名を指定します。メソッドは必ず指定した型の値をreturn
キーワードで指定する必要があります。
戻り値が必要ではない時はvoid
型を指定してください。その時はreturn
キーワードを書く必要はありません。
また、C#の文法上の都合から戻り値は一つしか書くことができません。複数の値を戻り値にしたい時はそのようなクラスを作るか、タプルを利用してください。
タプル(Tuple)について
タプルはクラス定義を簡略して使うことができる値型の型になります。
メソッドの中で簡単に複数の値をまとめることができる便利な型になります。
1 2 3 4 5 6 7 8 9 10 11 12 |
{//タプルの使い方 (int, float, string) a = (10, 1f, "ABC"); int n1 = a.Item1; // <- n1 == 10 float n2 = a.Item2; // <- n2 == 1f string n3 = a.Item3; // <- n3 == "ABC" } {//名前つきタプル (int No, string Name) b = (-1, "Tuple"); int n1 = b.No; // <- n1 == -1 string n2 = b.Name; // <- n2 == "Tuple" } |
ref returnとref local
戻り値の型名の前にref
キーワードを付けると、メソッドが所属しているクラスのメンバ変数への参照値を出力することができます。その際は呼び出し元でもref
キーワードを付ける必要があります。
かなり深く突っ込んだ使い方になりますので紹介だけに留めておきます。
引数について
引数はメソッドに渡す入力値になります。0個以上の好きな個数を指定できます。
引数の書き方は次のようになります。基本的に変数と同じになります。
<型名> <引数名>
引数のデフォルト値
引数にはデフォルト値を指定することができます。
デフォルト値付きの引数は定義の際、必ず引数の最後に書く必要がありますが、これはC#の文法上の都合で呼び出しの時に見分けがつかなくなるためです。最後に書くならいくつでも引数にデフォルト値をつけることができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class A { //デフォルト値付きの引数 public void Method(int a = 10) {} //必ず、引数の最後に書く必要がある。 public void Method2(int a, int b = 10); //aのように後にデフォルト値なしの引数を書くことはできない。 public void NG3(int a = 10, int b, float c); } var a = new A(); //呼び出しの時はデフォルト値付きのものは省略できる。 a.Method(); //<- OK, 引数aには10が渡される。 a.Method(-1); // <- OK. 引数aには-1が渡される。 a.Method2(10); // <- OK a.Method2(10, -234); // <- OK |
名前付き引数の渡し方
メソッドを呼び出す時には引数をメソッドで決められた順序で書く必要がありますが、名前付き引数を使うとある程度自由な順序で書くことができます。
- 名前付き引数の書き方:
<引数名> : <値>
名前付き引数を使用する時は引数名と’:’を先に書いた後に値を書きます。
名前付き引数は通常の引数と組み合わせて使うことができますが、その際の通常の引数はメソッドの定義と同じ位置に書く必要があります。
全て名前付き引数の場合は、自由な位置に書くことができます。
名前付き引数はデフォルト値付きの場合でも使えます。
1 2 3 4 5 6 7 8 9 10 11 |
class A { public void Method(int a, int b, int c) { ... } } var inst = new A(); //次の2つは同じ引数を渡している。 inst.Method(c: 100, a: 10, b: -1); inst.Method(10, -1, 100); inst.Method(a:10 , -1, c: 100); |
引数に渡す値についての注意点
メソッドの呼び出しの時に引数として渡す値は基本的にその値がコピーされてメソッドに渡されます。そのため、引数をメソッドの中で変更しても呼び出し側の値は変更されません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class A { public void Method(int a) { a += 2; //引数aの値を変更してみる。 } } var inst = new A(); int A = 10; //Methodの引数aに変数Aを渡す // 変数AをコピーしたものがMethod()の中で+2されている。 inst.Method(a); //値を確認してみると、変数Aの値は変わっていないことがわかる bool OK = A == 10; // <- OK == true bool Bad = A == 12; // <- Bad == false |
ただし、参照型の場合には注意が必要です。
参照型の中身は
- その構造がどのデータを指すのか
- 指しているデータの実態
の2つに分かれており、引数に渡す際はその内のどのデータを指すのかという情報がコピーされます。
このためメソッド内で参照型の引数のメンバを変更した時はデータの実態側に影響を与えるため、呼び出し側にもその影響が出ることになります。
例えば、以下のコードでは参照型であるクラスをメソッドの引数で渡しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class A { public void Method(Data data) { data.Value += 2; //Data型のValue変数の値を変更してみる。 } } class Data { public int Value = 0; } var inst = new A(); var d = new Data(); d.Value = 100; //Methodの引数dataに変数dを渡す // 変数dのどのデータを指しているのかという情報をコピーしたものがMethod()の中に渡される。 inst.Method(d); //値を確認してみると、変数dのメンバ変数の値が変わっていることがわかる bool OK = (d.Value == 102); // <- OK == true bool Bad = (d.Value == 100); // <- Bad == false |
また、次のコードのように参照型の引数の値を上書きすると、どのデータを指すのかという情報を変更することになります。そのため変更後にメンバの値を変えたとしても呼び出し側には影響を与えません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class A { public void Method(Data data) { data = new Data(); //引数dataに新しく作成した実態データを設定する。 data.Value = -1; //新しく作成した実態データのValueの値を変更している } } class Data { public int Value = 0; } var inst = new A(); var d = new Data(); d.Value = 100; //Methodの引数dataに変数dを渡す // 変数dのどのデータを指しているという情報をコピーしたものがMethod()の中に渡される。 inst.Method(d); //値を確認してみると、変数dのメンバ変数の値は変わっていないことがわかる bool OK = (d.Value == 100); // <- OK == true bool Bad = (d.Value == -1); // <- Bad == false |
引数につけることができるキーワード
引数にはいくつかのキーワードを付けることができます。
先に引数はコピーされてメソッドに渡されると説明しましたが、これらのキーワードを使用することでそれとは異なる渡し方ができます。
キーワードを付けることで引数の値を読み取り専用にしたり、呼び出し元には影響を与えられるようにできます。
in
:引数を読み取り専用の参照値として扱う。ref
:引数を参照値として渡す。out
:引数で渡した変数をメソッド内で設定するように指定する。
inキーワード付き引数
inキーワードを引数の前に書くと、その引数は読み取り専用の参照値として渡すようになります。
引数自体の値を変更できませんが、参照値なのでメンバは変更でき、呼び出し元にも影響がでます。
また、どこかで宣言した変数でないと渡せなくなるので注意してください。
struct
型などデータサイズが大きな値型をコピーすると処理負荷が高くなりますが、in
キーワードを付け参照型として扱うことでコピーを避けることができます。
inキーワードは呼び出し時には省略することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class A { public void Method(in int a, in Struct s, in Class c) { a = 10; // <- NG s = new Struct(); // <- NG c = new Class(); // <- NG s.Value = 10; // <- OK c.Value = 20; // <- OK } } class Class { public int Value; } struct Struct { public int Value; } var inst = A(); int a = 0; Struct s = new Struct(); Class c = new Class(); //呼び出しの時はinキーワードは省略できる。 inst.Method(a, in s, c); |
refキーワード付き引数
ref
キーワードを引数につけるとその引数を参照値として扱うようになり、メソッド内の変更をメソッド呼び出し側にも与えることができます。
in
キーワードと似ていますが、こちらは引数の値を変更することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
class A { //refキーワードは引数の前に書くこと public void Method(ref int a) { a += 2; } public void Method(ref Data data) { data = new Data(); //引数dataに新しく作成した実態データを設定する。 data.Value = -1; //新しく作成した実態データのValueの値を変更している } } class Data { public int Value = 0; } var inst = new A(); int A = 100; //Methodの引数aに変数Aを渡す // こちらでも引数として渡す値の前にrefを書くこと inst.Method(ref d); //値を確認してみると、変数Aの値が変わっていることがわかる bool OK = (A == 102); // <- OK == true bool Bad = (A == 100); // <- Bad == false //参照型の場合も同じ Data D = new Data(); D.Value = 100; var prev = D; //<- 引数に渡す前の実態データを指す情報をコピーしておく inst.Method2(ref D); bool OK2 = (D.Value == -1); bool Bad2 = (D.Value == 100); //実態データが置き換わっていることの確認 bool isEqual = (prev == D); // <- falseになる。 |
outキーワード付き引数
out
キーワードをつけると、戻り値以外にもその引数を介してメソッドからの出力を受け取れる様になります。
outキーワードをつけた引数は必ずそのメソッド内で値を設定する必要があるので注意してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
class A { //outキーワードは引数の前に書くこと public void Method(out int a) { a = 2; } public void Method2(out Data data) { data = new Data(); //引数dataに新しく作成した実態データを設定する。 data.Value = -1; //新しく作成した実態データのValueの値を変更している } } class Data { public int Value = 0; } var inst = new A(); int A = 100; //Methodの引数dataに変数dを渡す // こちらでも引数として渡す値の前にoutを書くこと inst.Method(out d); //値を確認してみると、変数Aの値が変わっていることがわかる bool OK = (A == 2); // <- OK == true bool Bad = (A == 100); // <- Bad == false //参照型の場合も同じ Data D; // <- 宣言のみでOK inst.Method2(out D); bool OK2 = (D.Value == -1); // Method2の中で値が設定されていることを確認している bool isInitial = (D != null); // <- true |
outキーワードの簡略化された書き方について
out
キーワードが指定された引数は呼び出しの中で直接、変数宣言を行うことができます。
1 2 3 4 5 6 7 8 9 10 11 |
class A { public void Method(out int b) { b = 100; } } var inst = new A(); // outキーワードは引数の中で変数宣言できる。 inst.Method(out int newValue); bool OK = (newValue == 100); // <- true |
メソッドのオーバーロード
C#では同じメソッド名を持つが引数が異なるメソッドを定義することができます。このことをメソッドのオーバーロード(Overload)と呼びます。
引数が異なるとみなされる条件は以下のものになります。
- 引数の型の順序が異なる。
in
、ref
、out
キーワードの有無。
戻り値だけが異なる場合はオーバーロードすることはできませんので注意してください。
なお、デフォルト値付き引数によって引数の数が見かけ上、一致してしまう場合でもオーバーロードできません。
1 2 3 4 5 6 7 8 9 10 11 |
class A { // オーバーロード可能なもの public int Method() { ... } public int Method(int a) { ... } public float Method(float a) { ... } public float Method(int a, int b) { ... } // NGなもの public void Method() { ... } public int Method(float a) { ... } } |
アローキーワード ‘=>’
アローキーワード(‘=>’)を使用してメソッドを書くと、カギ括弧とreturn文を省略してメソッドの中身を書くことができます。
アローキーワードはラムダ関数(※)などメソッドを簡単に書きたい時に使用されます。
(※ラムダ関数はメソッドの中に書くことができるメソッドになります。)
1 2 3 4 |
class A { //引数aを+10した値を返すメソッド public int Method(int a) => a + 10; } |
アロー演算子を使用した場合はメソッドの中身は必ず値を返す一文でなければならず、変数を宣言したり、二つ以上の文を書くことはできません。
どうしても書きたい時は?演算子を使用してください。
1 2 3 4 5 6 |
class A { public int Method(int a) => (a % 2) == 0 ? a * 20 : a; } |
【実践】Unityで実際にメソッドを作って使ってみよう!
それでは実際にUnityでメソッドを作って使ってみましょう!
これまででもStart()やUpdate()と既にメソッドを作っていましたが、今回は好きなメソッドを作って実際に呼び出してみましょう!
サンプルコードではChangeColor(Color, float)というメソッドを作って、Updateメソッドの中で呼び出しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
using UnityEngine; public class Sample : MonoBehaviour { public float Value = 0f; public Color Color = Color.white; public MeshRenderer Mesh; //引数に渡されたColorを引数vを使って新しいColorを作るメソッド Color ChangeColor(Color color, float v) { color.r = v; return color; } void Update() { //毎フレームValueとColorを使って新しい色を計算している。 var color = ChangeColor(Color, Value); if(Mesh != null) Mesh.material.color = color; } } |
まとめ
今回の記事ではメソッドについて学びました。簡単にまとめると以下のようになります。
- メソッドとはスクリプトの処理をまとめたもの。
- メソッドは入力値(引数)を受け取り、値を出力する(戻り値)。
- 何も値を出力しない時は
void
型を指定する。 - 引数に渡した値はコピーされるので、そのメソッド以外には影響を与えない。
- ただしクラス型など参照型を引数として与えると参照先のデータに影響が出る。
- メソッドに渡す際に元の値をコピーされたくない場合は
in
キーワードを使ういい。 - 引数自体にメソッドの処理内容の影響を与えたい時は
ref
、out
キーワードを指定する。 - 引数が異なる場合は同じ名前のメソッドがあってもいい(オーバーロード)
- アローキーワードを使うと制約があるが簡単にメソッドの中身を書くことができる。
以上になります。それでは次の記事に進んでいきましょう!
コメント