[C#] 52. Reflection機能を使い方 - Variable


Study / C#    作成日付 : 2021/10/15 19:27:37   修正日付 : 2021/10/15 19:27:37

こんにちは。明月です。


この投稿はC#でReflection機能を使い方 - Variableに関する説明です。


以前の投稿でReflectionに関するクラスと関数を使い方に関して説明しました。

link - [C#] 50. Reflection機能を使い方 - Class

link - [C#] 51. Reflection機能を使い方 - Method


Reflectionというのはクラスや関数でソースにインスタンス生成(new)や関数を呼び出し(method())の普通の仕方ではなく、動的にReflection機能を利用して生成及び呼び出す方法という意味です。

変数も同じ意味です。特にC#にはクラスの中で変数はオブジェクト指向プログラミング(OOP)によるメンバー変数はprivateで生成することが一般的です。

しかし、仕様により強制的に変数の値を変更するかユニットテスト(NUnit)を実施する時にデバックの資料で使う場合があります。

using System;

namespace Example
{
  // 例クラス
  class Node
  {
    // メンバー変数はprivate
    private int data;
    // コンストラクタ
    public Node(int data)
    {
      // コンストラクタからメンバー変数の値を格納
      this.data = data;
    }
    // コンソール出力関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine(data);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // インスタンス生成
      var node = new Node(10);
      // 結果は10
      node.Print();
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}


上の例は一般的なコーティングする方法でNodeクラスのインスタンスを生成する時、コンストラクタに10という値を入れてPrint関数を通ってコンソールに出力しました。

当然にMain関数にはNodeのインスタンスのメンバー変数のdataを参照することができません。コンストラクタからデータを入れること以外は値を変更するか値を取得することができなく、Print関数を通って出力だけできます。

using System;
using System.Reflection;

namespace Example
{
  // 例クラス
  class Node
  {
    // メンバ-変数はprivate
    private int data;
    // コンストラクタ
    public Node(int data)
    {
      // コンストラクタからメンバー変数の値を格納
      this.data = data;
    }
    // コンソール出力関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine(data);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // インスタンス生成
      var node = new Node(10);
      // NodeクラスのTypeを取得
      var nodeClz = typeof(Node);
      // Nodeクラスのdata変数を取得(privateやprotectedタイプ | インスタンスタイプ(staticではないメンバー変数)
      var field = nodeClz.GetField("data", BindingFlags.NonPublic | BindingFlags.Instance);
      // インスタンスのメンバー変数の値を取得
      var data = field.GetValue(node);
      // コンソールに出力
      Console.WriteLine(data); ;
      // インスタンスメンバー変数の値を設定
      field.SetValue(node, 100);
      // 関数を呼び出す。
      node.Print();
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}


上の例でtypeof(Node)でNodeクラスのType構造を取得した後にdataのメンバー変数を取得しました。

そしてコンソールに出力するとメンバー変数の値が出力しました。その後、SetValueを通って値を格納してPrint関数を呼び出したら変更された値が出力されました。


ここで見るとReflectionを通って関数を取得する場合とすごく似ています。実は同じですね。

using System;
using System.Reflection;

namespace Example
{
  // 例クラス
  class Node
  {
    // プロパティ
    public int Data
    {
      get; set;
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // インスタンス生成
      var node = new Node();
      // NodeクラスのTypeを取得
      var nodeClz = typeof(Node);
      // Nodeクラスのすべての変数を取得
      foreach (var field in nodeClz.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
      {
        // 変数名と値を出力
        Console.WriteLine($"{field.Name} = {field.GetValue(node)} ");
        // 変数名にDataが含めているなら
        if (field.Name.Contains("Data"))
        {
          // 設定
          field.SetValue(node, 1000);
        }
      }
      // nodeインスタンスのプロパティのDataを出力
      Console.WriteLine(node.Data);

      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}


上の例はNodeクラスにメンバー変数がありません。ただプロパティだけあります。

でも、私がReflectionを通ってNodeクラスのメンバー変数を取得するとDataのbackingfieldというメンバー変数があります。つまり、プロパティでget、setだけで設定してコンパイルしたら自動にprivate変数が生成されることを確認できます。

つまり、プロパティだけ作成しても内部的にはprivateメンバー変数が生成してOOP規約に合わせることを確認できます。

値を設定してプロパティで値を取得すると値が変更されたことを確認できます。


ReflectionはC#のアクセス修飾子を無視してデータを取得するか設定することができます。

一般プログラムを作成する時には使うことをお勧めしないです。可読性やデータ無欠性に悪くなりますね。ただ、NUnitテストやデバックプログラムを作成する時に使ったらよいですね。


ここまでC#でReflection機能を使い方 - Variableに関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

#C#
最新投稿