C#でインスタンス間のデータの受け渡しをしてみた
.Netになる前のVisual BasicでWindowsのプログラミングに入ったので、複数のプロシージャで変数を共有しようと思ったら迷わずグローバル変数を使ってました。カウンタ用の変数以外は全部グローバルみたいな。プログラミングを生業にしていたわけでもないし(今でも)、大きなデータを扱うこともなかったので、特にそれで問題ありませんでした。 で、C#でも同じノリでグローバル変数を使おうと思ったのですが、グローバル変数って無いんですね。「いつどのインスタンスがデータを書き換えるかわからない」と言われれば、確かに使わない方が良さそうですが。 というわけで、インスタンス間でのデータの受け渡しをちょっとまじめに勉強しなくてはならないかなと思いました。というメモです。
目次
データの受け渡し方
データを渡して何がしたい?
アプリ内でデータ保持用のインスタンスを作ります。そのインスタンスで保持されたデータを、他のインスタンスから書き換えるようにします。
具体的には、「データ保持を専用のインスタンスで行っていて、ボタンをクリックしたらそのデータを書き換える」というイメージです。
受け渡すデータの型
C#のデータの型には、値型と参照型というのがあります。参照型というのは、メモリ内にデータの実体を置いておいて、変数にはその実体の場所の情報を入れておくというものです。
今回は、渡された側のインスタンスが別のインスタンスが保持しているデータを書き換えるようにしたいので、参照型を使います。値型と参照型についてはMSDNの解説を参照してください。
データを渡す方法
クラスから作成したインスタンスは参照型です。ですから、データ保持用のクラスを定義してそのインスタンスを作成し、そのインスタンスにデータを保持します。
データ保持用のインスタンスの「参照」を、データ書き換え用のインスタンスに渡します。データ書き換え用のインスタンスは、渡された「参照」を使ってデータの実体を書き換えます。
インスタンスに「参照」を渡す方法として考えられるのは下記です。(初心者です。他にあれば教えていただきたいです。)
インスタンスのフィールドに渡す(フィールドは非公開にするのが推奨なので、この方法はMicrosoft的には非推奨だと思います。)
インスタンスのプロパティに渡す
インスタンスのメソッドの引数として渡す
試してみた
DataStoreクラスのインスタンスstocker(データ保持用)に設定した文字列を、編集用の各クラスClassA/ClassB/ClassCのインスタンスが編集してstockerのデータを更新するようなコンソールアプリを作ってみたいと思います。 データ保持用のクラスはどの方法でも共通です。
namespace class_trial
{
    public class DataStore
    {
        public string TextString; // 公開フィールド
    }
}
フィールドで渡してみた
まずフィールドに渡してみました。
using System;
namespace class_trial
{
    class Program
    {
        static void Main(string[] args)
        {
            DataStore stocker = new DataStore();
            stocker.TextString = "初期値"; // データ保持用のインスタンスに初期データを設定する
            Console.WriteLine("保持されているデータ : " + stocker.TextString); // 編集前のデータを表示する
            // フィールドを使ってデータのやりとりをする
            ClassA editorA = new ClassA();
            editorA.TextData = stocker;
            editorA.EditText();
            // データ保持用インスタンスの値を出力する
            Console.WriteLine("保持されているデータ : " + stocker.TextString); // 編集後のデータを表示する
        }
    }
}
namespace class_trial
{
    public class ClassA
    {
        public DataStore TextData;
        public void EditText()
        {
            TextData.TextString = TextData.TextString + "+フィールドで編集した値";
        }
    }
}
実行すると、下図の様になります。 
元のデータを書き換えられました。でも、フィールドに「参照」を渡した後で書き換え用のメソッドの実行指示が必要です。フィールドってデータであってアクションしませんからね。というわけで、この方法はあまり使わなさそうです。
プロパティで渡してみた
プロパティに渡してみました。
using System;
namespace class_trial
{
    class Program
    {
        static void Main(string[] args)
        {
            DataStore stocker = new DataStore();
            stocker.TextString = "初期値"; // データ保持用のインスタンスに初期データを設定する
            Console.WriteLine("保持されているデータ : " + stocker.TextString); // 編集前のデータを表示する
            // プロパティを使ってデータのやりとりをする
            ClassB editorB = new ClassB();
            editorB.TextData = stocker;
            // データ保持用インスタンスの値を出力する
            Console.WriteLine("保持されているデータ : " + stocker.TextString); // 編集後のデータを表示する
        }
    }
}
namespace class_trial
{
    public class ClassB
    {
        private DataStore _text;
        public DataStore TextData
        {
            set
            {
                _text = value;
                _text.TextString = _text.TextString + "+プロパティで編集した値";
            }
        }
    }
}
setアクセサーに書き換え用のコードを書きました。こうすると、書き換え用のインスタンスのプロパティに「参照」を渡したら自動的に参照先の実データが書き換わります。
実行結果は下図の様になります。 
元データは書き換わりました。フィールドに渡したときと違って、プロパティに渡した時点で書き換えられます。プロパティというと属性というイメージなのですが、インスタンスの属性にある値を入れると別のインスタンスのデータが書き換わるっていうのは、感覚的にしっくりくるのかなという感じです。
メソッドで渡してみた
メソッドに渡してみました。
using System;
namespace class_trial
{
    class Program
    {
        static void Main(string[] args)
        {
            DataStore stocker = new DataStore();
            stocker.TextString = "初期値"; // データ保持用のインスタンスに初期データを設定する
            Console.WriteLine("保持されているデータ : " + stocker.TextString); // 編集前のデータを表示する
            // メソッドを使ってデータのやりとりをする
            ClassC editorC = new ClassC();
            editorC.EditText(stocker);
            // データ保持用インスタンスの値を出力する
            Console.WriteLine("保持されているデータ : " + stocker.TextString); // 編集後のデータを表示する
        }
    }
}
namespace class_trial
{
    class ClassC
    {
        public void EditText(DataStore textData)
        {
            textData.TextString = textData.TextString + "+メソッドで編集した値";
        }
    }
}
まとめ
「データのコピーをいちいち渡してたらメモリの無駄遣いじゃない?グローバル変数にしておいてみんなでアクセスした方が効率よくない?」なんて考えてましたが、データへの「参照」の受け渡しでデータの書き換えができるのですね。C言語から入った人にはフツーの考え方なのでしょうか。 プロパティに渡すかメソッドに渡すかについては、クラスの設計と用途に依るものと思います。フィールドは非公開が推奨だそうなので、フィールドに渡すくらいならプロパティに何もしないsetアクセサーを設定してプロパティに渡した方が良いのかもしれません。
公開日
広告
C#カテゴリの投稿
- C#でMVVMって何でしょう
 - C#でPDFを表示する(WPF)
 - C#でアプリのログを記録してみる
 - C#でアプリの設定を保存する
 - C#でインスタンスをプログラムで作ってみた(Activator.CreateInstance編)
 - C#でインスタンスをプログラムで作ってみた(Type.InvokeMember編)
 - C#でインスタンス間のデータの受け渡しをしてみた
 - C#でウェブサイトのソースを取得してみた
 - C#でエラーの処理をする
 - C#でクラスのフィールド宣言とコンストラクターでの初期化はどっちが優先する?
 - C#でスタックを使って逆ポーランド記法の計算をしてみた
 - C#で数式を中置記法から後置記法(逆ポーランド記法)に変換してみた(三角関数編)
 - C#で選択(switch-case編)
 - C#のWPFでデータバインディング
 - C#のXAMLでメニューとステータスバーのレイアウトをしてみた
 - C#のアプリの情報を表示してみた
 - C#のクラスとインスタンスとオブジェクト
 - C#の反復処理(foreach編)
 - C#の命名規則
 - C#へのMicrosoft.TeamFoundation.Controlsの参照の追加について
 
