C# in ref outの挙動の実例

クラス設計をたまにすると参照修飾子なしと 参照修飾子refの使い分がはっきりしないので表を作成しました。

# in ref out 一覧表

string クラスの void func12(string a); 挙動だけが特別でその他のすべての挙動は感覚的な動作と一致するようです。
参照修飾子inの動作はVisual Stdio 2015なので未確認です。

| 関数                            |  変更時の呼び出し元への影響 | 新しい変数の割り当ての呼び出し元への影響 |
|---------------------------------|--------------------------|---------------|
|  void func11(int a)             | しない                    |  ---          |
|  void func12(string a);         | しない *1                 |しない         |
| void func13(StringBilder a);    | する                      |しない          |
| void func14(ClassC a);          | する                      |しない          |
| void func15(StructS a);         | しない                    |しない |
| void func16(int[] a);           | する                      | しない |
| **参照修飾子 in**                |                           |                |
|void func21( in int a);          |---                        |---            |
|void func22( in string a);       |---                        |---            |
|void func23( in StringBilder a); |---                        |---            |
|void func24( in ClassC a);       |---                        |---            |
|void func25( in StructS);        |---                        |---            |
|void func26( in int[] a);        |---                        |---            |
| ** 参照修飾子 ref **             |---------------------------|---------------|
| void func31( ref int a);        | する                      | --- |     
| void func32( ref string a);     | する  *1                  | する |    
| void func33( ref StringBilder a);| する                     | する |
| void func34( ref ClassC a);     | する                      | する |
| void func35( ref StructS a);    | する                      | する |
| void func36( ref int[] a);      | する                      | する |                        
| **---------------------------------|---------------------------|---------------|
| void func41( out int a);        |  必須                       | ---           |
| void func42( out string a);     |  ---                       | 必須 |
| void func43( out StringBilder a); | ---                       | 必須--- |
| void func44( out ClassC a);      | ---                        | 必須--- |
| void func45( out StructS a);     | ---                        | 必須--- |
| void func46( out int[] a);       | ---                        | 必須--- |  

*1) string はインスタンス不変で実装されています。まは、同様にものにSystem.Collections.Immutable 名前空間で定義さている System.Collections.Immutable.ImmutableList などがあります。




#ソースコード

```teat01.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace ConsoleApplication1
{
    public class ClassC
    {
        public int x;
        public int y;
    }
    public struct StructS
    {
        public int x;
        public int y;
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("\ntest in ref out ");

            Console.WriteLine("\n参照test in ref out ");
            test11();
            test12();
            test13();
            test14();
            test15();
            test16();
            //
            test21();
            test22();
            test23();
            test24();
            test25();
            test26();

            test31();
            test32();
            test33();
            test34();
            test35();
            test36();

            test41();
            test42();
            test43();
            test44();
            test45();
            test46();

            Console.WriteLine("Enter Key !");
            Console.ReadLine();
        }
        static void test11()
        {
            Console.WriteLine("\ntest11 func11(int a)");
            int x = 1;
            Console.WriteLine("befor : x= {0}", x);
            func11(x);
            Console.WriteLine("after : x= {0}", x);
        }

        static void test12()
        {
            Console.WriteLine("\ntest12 func12(string a)");
            string s = "Hello World";
            Console.WriteLine("befor : s = {0}", s);
            func12(s);
            Console.WriteLine("after : s = {0}", s);
        }
        static void test13()
        {
            Console.WriteLine("\ntest13  func13(StringBiulder a)");
            StringBuilder s = new StringBuilder("StringBuilerの初期値");
            Console.WriteLine("befor : s = {0}", s);
            func13(s);
            Console.WriteLine("after : s = {0}", s);
        }
        static void test14()
        {
            Console.WriteLine("\ntest14  func14(ClassC a)");

            ClassC c = new ClassC() { x = 1, y = 2 };
            Console.WriteLine("befor : x= {0}, y={1}", c.x, c.y);
            func14(c);

            Console.WriteLine("after : x= {0}, y={1}", c.x, c.y);
        }
        static void test15()
        {
            Console.WriteLine("\ntest15  func15(StructS a)");

            StructS s = new StructS() { x = 1, y = 2 };
            Console.WriteLine("befor : x= {0}, y={1}", s.x, s.y);
            func15(s);
            Console.WriteLine("after : x= {0}, y={1}", s.x, s.y);
        }
        static void test16()
        {
            Console.WriteLine("\ntest16  func16(int[] a)");
            int[] a = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
            Console.WriteLine($"befor : {string.Join(",", a)}");       // 

            func16(a);
            Console.WriteLine($"after : {string.Join(",", a)}");       // 
        }
        static void test21() { }
        static void test22() { }
        static void test23() { }
        static void test24() { }
        static void test25() { }
        static void test26() { }

        static void test31()
        {
            Console.WriteLine("\ntest31  func31(ref int a)");
            int x = 1;

            Console.WriteLine("befor : x= {0}", x);
            func31(ref x);
            Console.WriteLine("after : x= {0}", x);
            func31_2(ref x);
            Console.WriteLine("after2 : x= {0}", x);
        }
        static void test32()
        {
            Console.WriteLine("\ntest32  func32(ref string a)");
            String s = "stringです";
            Console.WriteLine("befor : s= {0}", s);
            func32(ref s);
            Console.WriteLine("after : s= {0}", s);
            func32_2(ref s);
            Console.WriteLine("after2 : s= {0}", s);
        }
        static void test33()
        {
            Console.WriteLine("\ntest33  func32(ref StringBilder a)");
            StringBuilder s = new StringBuilder("StringBuilerの初期値");
            Console.WriteLine("befor : s = {0}", s);
            func33(ref s);
            Console.WriteLine("after : s = {0}", s);
            func33_2(ref s);
            Console.WriteLine("after2 : s = {0}", s);
        }
        static void test34()
        {
            Console.WriteLine("\ntest34  func34(ref ClassC a)");
            ClassC c = new ClassC() { x = 1, y = 2 };
            Console.WriteLine("befor : x= {0}, y={1}", c.x, c.y);
            func34(ref c);
            Console.WriteLine("after : x= {0}, y={1}", c.x, c.y);
            func34_2(ref c);
            Console.WriteLine("after2 : x= {0}, y={1}", c.x, c.y);
        }
        static void test35()
        {
            Console.WriteLine("\ntest35  func35(ref StructS a)");
            StructS c = new StructS() { x = 1, y = 2 };
            Console.WriteLine("befor : x= {0}, y={1}", c.x, c.y);

            func35(ref c);
            Console.WriteLine("after : x= {0}, y={1}", c.x, c.y);
            func35_2(ref c);
            Console.WriteLine("after2 : x= {0}, y={1}", c.x, c.y);


        }
        static void test36()
        {
            Console.WriteLine("\ntest36  func36(ref int[] a)");
            int[] a = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
            Console.WriteLine($"befor : {string.Join(",", a)}");       // 

            func36(ref a);
            Console.WriteLine($"after : {string.Join(",", a)}");       // 
            func36_2(ref a);

            Console.WriteLine($"after2 : {string.Join(",", a)}");       // 
        }

        static void test41() { }
        static void test42() { }
        static void test43() { }
        static void test44() { }

        static void test45() { }
        static void test46() { }
        // 
        // -------------------- 参照識別子 無し。
        static void func11(int a) {
            a = a + a;                      // 呼び出し元に反映されない。
        }
        static void func12(String a) {
            a = "ABC";                      // 呼び出し元に反映されない。
            a = new string((char)0x30, 1);  // 呼び出し元に反映されない。
            a = "XYZ";                      // 呼び出し元に反映されない。
        }
        static void func13(StringBuilder a)
        {
            a.Append(a);                           // 呼び出しもとに反映される。 
            a = new StringBuilder("Changeです");   // 反映されない。
        }

        static void func14(ClassC a)
        {
            a.x = 22;                              // 呼び出し元に反映される。
            a.y = 33;
            a = new ClassC() { x = 44, y = 55 };   // 呼び出し元に反映されない。 
        }
        static void func15(StructS a)
        {
            a.x = 66;                              // 呼び出し元に反映されない。
            a.y = 77;
            a = new StructS() { x = 88, y = 99 };  // 呼び出し元に反映されない。
        }

        static void func16(int[] a)
        {
            a[0] = 22;              // 呼び出し元に反映させる。
            a = new int[5];         // 呼び出し元に反映されない。
            a[0] = 33;              // 呼び出い元に反映されない。
        }

        // 参照識別子 in  
        // void func21(in int a){} 
        // void func22(in String a){}
        // void func23(int Object a){}
        // void func24(int int[] a){}


        // ref 
        static void func31(ref int a)
        {
            a = a + a;　　　　　　  // 呼び出し元に反映される。
        }
        static void func31_2(ref int a)
        {
            a = new int();         // 呼び出し元に反映される。
            a = 9;
        }
        static void func32(ref string a)
        {
            a = a + a;             // 呼び出し元に反映される。
        }
        static void func32_2(ref string a)
        {
            a = "new Stringです";  // 呼び出し元に反映される。
        }
        static void func33(ref StringBuilder a)
        {
            a.Append(a);           // 呼び出し元に反映される。
        }
        static void func33_2(ref StringBuilder a)
        {
            a = new StringBuilder(" new StringBilder");  // 呼び出し元に反映される。
        }

        static void func34(ref ClassC a)
        {
            a.x = a.x * 2;          // 呼び出し元に反映される。
            a.y = a.y * 2;
        }
        static void func34_2(ref ClassC a)
        {
            a = new ClassC() { x = 11, y = 12 };   // 呼び出し元に反映される
        }


        static void func35(ref StructS a)
        {
            a.x = a.x * 3;                          // 呼び出し元の反映される。
            a.y = a.y * 3;
        }
        static void func35_2(ref StructS a)
        {
            a = new StructS() { x = 102, y = 202 };  // 呼び出し元に反映される。
        }


        static void func36(ref int[] a)
        {
            a[0] = 22;                                  // 呼び出し元に反映される。
        }
        static void func36_2(ref int[] a)
        {
            a = new int[] { 101, 102, 103, 104, 105 };    // 呼び出し元に反映される
        }


        // out 
        static void func41(out int a) {
            a = 41;                                 // aに値を設定しないとエラーになる。
        }
        static void func42(out string a) {
            a = "string func42";                    // aに値を設定しないとエラーになる。
        }
        static void func42_2(out string a)
        {
            a = null;                               // nullで設定できる。
        }

        static void func43(out StringBuilder a)
        {
            a = new StringBuilder("stringbilder func43");   // 値を設定しないとエラーになる。
        }
        static void func43_2(out StringBuilder a)
        {
            a = null;                                   // nullでも設定できる。
        }
        static void func44(out ClassC a) {
            a = new ClassC() { x = 44, y = 440 };           // 値を設定しないとエラーになる。
        }
        static void func44_2(out ClassC a)
        {
            a = null;                                   // 値を設定しないとエラーなる。
        }
        static void func45(out StructS a)
        {
            a = new StructS() { x = 4, y = 5 };
            // a = null;   // nullは「非Null許容型であるため、NullをStrictSに変換できません」となる。
        }

        static void func46(out int[] a)
        {
            a = new int[]{ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55 };
        }
        static void func46_2(out int[] a)
        {
            a = null;
        }
    }
}

```
#結果
```out.txt
test in ref out

参照test in ref out

test11 func11(int a)
befor : x= 1
after : x= 1

test12 func12(string a)
befor : s = Hello World
after : s = Hello World

test13  func13(StringBiulder a)
befor : s = StringBuilerの初期値
after : s = StringBuilerの初期値StringBuilerの初期値

test14  func14(ClassC a)
befor : x= 1, y=2
after : x= 22, y=33

test15  func15(StructS a)
befor : x= 1, y=2
after : x= 1, y=2

test16  func16(int[] a)
befor : 1,2,3,4,5,6,7,8,9,0
after : 22,2,3,4,5,6,7,8,9,0

test31  func31(ref int a)
befor : x= 1
after : x= 2
after2 : x= 9

test32  func32(ref string a)
befor : s= stringです
after : s= stringですstringです
after2 : s= new Stringです

test33  func32(ref StringBilder a)
befor : s = StringBuilerの初期値
after : s = StringBuilerの初期値StringBuilerの初期値
after2 : s =  new StringBilder

test34  func34(ref ClassC a)
befor : x= 1, y=2
after : x= 2, y=4
after2 : x= 11, y=12

test35  func35(ref StructS a)
befor : x= 1, y=2
after : x= 3, y=6
after2 : x= 102, y=202

test36  func36(ref int[] a)
befor : 1,2,3,4,5,6,7,8,9,0
after : 22,2,3,4,5,6,7,8,9,0
after2 : 101,102,103,104,105
Enter Key !
```
#参考リンク
[@IT in／out／refパラメーター修飾子の違いとは？［C#］](http://www.atmarkit.co.jp/ait/articles/1804/25/news021.html)