C#で渡された配列を加工して配列で返す関数について注意すること

C#で配列を渡す場合は参照渡しになるということを試してみました。

目次

  1. 配列を返す関数
  2. 配列は参照渡しである
  3. 関数(メソッド)で配列を加工して戻したい
    1. 配列を=で複製すると参照が複製される
    2. cloneメソッドで配列を複製する

配列を返す関数

配列を作って、その内容をコンソールに出力するアプリです。

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] mat = ArrayTest();

            // コンソールに出力する
            foreach (var p in mat)
            {
                Console.WriteLine(p);
            }
            // コンソールを閉じないようにする
            Console.ReadKey();
        }

        private static string[] ArrayTest()
        {
            // 配列を作成する
            string[] ret = { "ほげ", "ぴよ", "ごま" };

            return ret;
        }
    }
}

実行すると、下図のように配列の中身が一行ずつ表示されます。

元の配列の表示

配列は参照渡しである

C#では配列は参照渡しをされるので、メソッドに配列を渡してメソッド内でその配列を処理すると、もとの配列のデータが書き換えられます。

下記のプログラムでは、Mainで作成した配列をメソッドに引数として渡します。メソッド内では与えられた配列の要素を変更します。そして、メソッドを実行してから元の配列を表示すると、配列のデータが書き換えられていることがわかります。

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] mat = { "ほげ", "ぴよ", "ごま" };

            ArrayTest(mat);

            // コンソールに出力する
            foreach (var p in mat)
            {
                Console.WriteLine(p);
            }
            // コンソールを閉じないようにする
            Console.ReadKey();
        }

        private static void ArrayTest(string[] ret)
        {
            ret[0] = "hoge";
        }
    }
}

実行結果はこうなります。関数「ArrayTest」は何も返さない関数ですが、関数内で配列の要素を変更しています。そうしたら元の配列の「ほげ」が「hoge」に書き換えられてますね。

参照渡しの例

関数(メソッド)で配列を加工して戻したい

元々の配列をメソッドに渡して、メソッド内で加工して戻り値として配列を戻したいという場合があります。

配列を=で複製すると参照が複製される

メソッド内で=を使って配列を別の変数に代入すると、うまくいきません。これは、=では配列の参照が渡されて、実態のデータはコピーされないからです。

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] mat = { "ほげ", "ぴよ", "ごま" };
            Console.WriteLine("元の配列");
            foreach (var p in mat)
            {
                Console.WriteLine(p);
            }

            string[] ret = ArrayTest(mat);

            Console.WriteLine("メソッド実行後の元の配列");
            foreach (var p in mat)
            {
                Console.WriteLine(p);
            }
            Console.WriteLine("メソッドの戻り値の配列");
            foreach (var p in ret)
            {
                Console.WriteLine(p);
            }
            Console.ReadKey();

        }

        static string[] ArrayTest(string[] arr)
        {
            string[] ret = arr;

            ret[0] = "hoge";

            return ret;
        }
    }
}

実行結果は下図のようになります。メソッド実行後に元の配列のデータが書き換わってしまっていますね。=を使って配列をコピーしようとしても、元の配列への参照が渡されるだけなのですね。

イコールでコピーした例

cloneメソッドで配列を複製する

呼び出し先にメソッド内で配列をcloneします。

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] mat = { "ほげ", "ぴよ", "ごま" };
            Console.WriteLine("元の配列");
            foreach (var p in mat)
            {
                Console.WriteLine(p);
            }

            string[] ret = ArrayTest(mat);

            Console.WriteLine("メソッド実行後の元の配列");
            foreach (var p in mat)
            {
                Console.WriteLine(p);
            }
            Console.WriteLine("メソッドの戻り値の配列");
            foreach (var p in ret)
            {
                Console.WriteLine(p);
            }
            Console.ReadKey();

        }

        static string[] ArrayTest(string[] arr)
        {
            var ret = (string[])arr.Clone();

            ret[0] = "hoge";

            return ret;
        }
    }
}

実行結果はこうなります。

cloneでコピーした例

元の配列の要素を書き換えること無く、メソッド内で配列を処理して戻すことができました。

公開日

広告