Visual C# 2013 パーフェクトマスター 7章 メソッド

[Amazon.co.jp： VisualC#2013ﾊﾟｰﾌｪｸﾄﾏｽﾀｰ (Perfect Master SERIES): 金城 俊哉: 本](http://www.amazon.co.jp/VisualC-2013%EF%BE%8A%EF%BE%9F%EF%BD%B0%EF%BE%8C%EF%BD%AA%EF%BD%B8%EF%BE%84%EF%BE%8F%EF%BD%BD%EF%BE%80%EF%BD%B0-Perfect-Master-SERIES/dp/4798040193)

自分の不明点をとりまとめ。理解できているところは割愛。

# メソッドの戻り値とパラメーターの設定
実行すべき一連の手続きをまとめたものをメソッドと呼ぶ。
下記の場合はmainメソッドに3つのステートメント。

```csharp
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("STEP1");
            Console.WriteLine("STEP2");
            Console.WriteLine("STEP3");
        }
    }
```

## メソッドの戻り値
returnステートメントで戻り値を設定できる

```csharp
    class Program
    {
        static string Test()
        {
            return ("hogehoge");
        }
 
        static void Main(string[] args)
        {
            String str = Test();
            Console.WriteLine(str);
        }
    }
```

## パラメーター
メソッドに任意の値を渡して処理を行う

```csharp
        static int a(int m)
        {
            return (m + 10);
        }

        static void Main(string[] args)
        {
            int z = a(5);
            Console.WriteLine(z);
        }
    }
```

## 値渡しと参照渡し
### refによる参照渡し
値そのものを渡す**値渡し**と値を参照する情報を渡す**参照渡し**がある
上述のパラメータ指定は値渡し。refをつける。

| 区分 | 値型 |  参照型
| ---- | ---- | ----
| 値渡し | 値のコピー | 参照情報のコピー
| 参照渡し | 値型変数のアドレス | 参照型変数のアドレス

```csharp
    class Program
    {
        static void Pro_1(int n1,int[] n2)
        {
            n1 += 1;
            n2[0] += 1;
        }

        static void Pro_2(ref int v1, ref int[] v2) // refで参照渡し
        {
            v1 += 1;
            v2[0] += 1;
        }

        static void Main(string[] args)
        {
            int a1 = 10, a2 = 10;
            int[] ary1 = { 1, 2, 3 };
            int[] ary2 = { 1, 2, 3 };

            Pro_1(a1, ary1);
            Console.WriteLine(a1);
            Console.WriteLine(ary1[0]);

            Pro_2(ref a2, ref ary2);
            Console.WriteLine(a2);
            Console.WriteLine(ary2[0]);

            // 10
            // 2
            // 11
            // 2
            // Pro1は値渡しなのでPro1で加算してもMainに反映されない。
            // Pro2は参照渡しなのでMainに反映される。
            // 配列は参照渡しなので加算される。
        }
```
### outで参照渡し
refステートメントの場合、変数を初期化しておく必要があるが、outメソッドの場合は初期化不要。
但しメソッド内で初期値を宣言しておくこと。

```csharp
        static void Pro_2(out int v1) // refで参照渡し
        {
            v1 = 10;
        }

        static void Main(string[] args)
        {
            int a1 = 10, a2;
            int[] ary1 = { 1, 2, 3 };
            int[] ary2 = { 1, 2, 3 };

            Pro_2(out a2);
            Console.WriteLine(a2);
        }
```


# メソッドの呼び出し式

## インスタンスメソッドの呼び出し

```csharp
    class Program
    {
        class A
        {
            public int Num;         // フィールドの宣言
            public int Add(int p)   // インスタンスメソッド
            {
                return Num + p;
            }
        }

        static void Main(string[] args)
        {
            var obj1 = new A();
            var obj2 = new A();
            obj1.Num = 10;
            obj2.Num = 100;

            Console.WriteLine(obj1.Add(2));
            Console.WriteLine(obj2.Add(2));
        }
    }
```

## 静的メソッドの呼び出し

```csharp
    class Program
    {
        public static int Add(int p1, int p2)   // 静的メソッド宣言
        {
            return p1 + p2;

        }

        static void Main(string[] args)
        {
            Console.WriteLine(Add(10, 20));
        }
    }
```

## デリゲート経由のメソッド呼び出し
デリゲート型とはメソッドを扱うための型。デリゲート型にはメソッドの参照が格納されるので、デリゲート型の変数をメソッド名のかわりに使ってメソッドを呼び出すことができる。

```csharp
    class Program
    {
        delegate void Del();
        static void Disp()      // 文字列表示のメソッド
        {
            Console.WriteLine("hogehoge");
        }

        static void a(Del call) // デリゲート型のパラメーターを持つメソッド
        {
            call();
        }

        static void Main(string[] args)
        {
            a(Disp);            // 引数にDispメソッドを指定してaメソッドを実行
        }
    }
```
mainメソッドはaメソッドに処理を委譲し、aメソッドは適切なメソッドの呼び出しを行う。
このようなデリゲートを利用した処理の委譲はイベントに対応したメソッド呼び出し等に利用される。

## 定義済みのデリゲート型
.NET Frameworkには規定のデリゲート型であるActionとFuncが用意されている。
戻り値がない場合はAction、戻り値がある場合はFuncを利用すればデリゲート型の宣言が不要になる。

```csharp
    class Program
    {
        static void Disp()      // 文字列表示のメソッド
        {
            Console.WriteLine("hogehoge");
        }

        private static void a(Action call) // デリゲート型のパラメーターを持つメソッド
        {
            call();
        }

        static void Main(string[] args)
        {
            a(Disp);            // 引数にDispメソッドを指定してaメソッドを実行
        }
    }
```

## ラムダ式
デリゲート名だけで呼び出せるようにし、コードを簡略化する

### デリゲート型の変数にメソッドを格納する

```csharp
    class Program
    {
        static void Disp()
        {
            Console.WriteLine("hogehoge");
        }

        static void Main(string[] args)
        {
            // デリゲート型のactにDispメソッドを格納。
            // 戻り値がないのでAction。戻り値がある場合はFunc。
            Action act = Disp;  
            act();
        }
    }
```

### ラムダ式で記述

```csharp
    class Program
    {
        static void Main(string[] args)
        {
            Action act = () =>
                {
                    Console.WriteLine("hogehoge");
                };

            act();
        }
    }
```

### ラムダ式の記述

> デリゲート型 デリゲート名 = (パラメーターのリスト)｛実行するステートメント｝

### パラメーターリストを設定したラムダ式

```csharp
    class Program
    {
        static void Main(string[] args)
        {
            Action<string> act = (x) =>
                {
                    Console.WriteLine(x);
                };

            act("hogehoge");
        }
    }
```

### 値を返すラムダ式
デリゲート型のFuncを使え歯ラムダ式から戻り値が取得できる

```csharp
        static void Main(string[] args)
        {
            int i = 1000;
            double d = 3.14159;
            Func<int, double, double> func = (x, y) =>
                {
                    return x * y;
                };

            double result = func(i, d);
            Console.WriteLine(result);
        }
```

Funcは値を返すデリゲート型なので戻り値の設定が必要。
型パラメーターのあとで戻り値の型を指定する

> Func<パラメーターの型, 戻り値の型>

### ラムダ式における変数の参照
メソッド内で宣言された変数はメソッド内のみで有効だが、ラムダ式の場合はラムダ式を含むメソッドの変数を直接参照できる。

```csharp
        static void Main(string[] args)
        {
            //int num = 100;
            //Func<int, int> func = (a) =>
            //    {
            //        return a * 2;
            //    };
            //num = func(num);
            //Console.WriteLine(num);

            //書き換え

            int num = 100;
            Action act = () =>
                {
                    num *= 2;
                };
            act();
            Console.WriteLine(num);
        }
```

### ラムダ式を1行でまとめる

```csharp
            Func<int, double, double> func = (x, y) =>
                {
                    return x * y;
                };

            // 1行で書き換え

            Func<int, double, double> func = (x, y) => (x * y);
```

## メソッドの戻り値をクラスで返す
戻り値の数が多い場合は戻り値の型をクラスにすると便利。

```csharp
    class Customer          // 戻り値用のクラスを定義
    {
        public int Id;      // 戻り値
        public string Name; // 戻り値
    }

    class Program
    {
        private static Customer getData()
        {
            return new Customer()
            {
                Id = 1001,
                Name = "hoge",
            };
        }

        static void Main(string[] args)
        {
            var dt = getData();
            Console.WriteLine(dt.Id + " " + dt.Name);
        }
    }
```

# 条件演算子を利用したifステートメント
条件演算子を利用するとifステートメントが短縮できる

> 条件式 ? 式が成立した時の値 : 成立しなかった時の値;

```csharp
    class Program
    {
        static void Main(string[] args)
        {
            bool b = true;
            string str;

            if (b == true) 
            {
                str = "OK";
            }
            else
            {
                str = "NG";
            }
            Console.WriteLine(str);


            if (b) str = "OK"; else str = "NO";
            Console.WriteLine(str);

            if (b) str = b ? "OK" : "NO";
            Console.WriteLine(str);
            // 条件演算子
        }
    }
```

# 疑問点
1. デリゲート型の具体的な用途がわからない
2. ラムダ式の用途がわからない。同一メソッド内で繰り返し処理を行う場合に別メソッドにしなくてよいということ？