CodeGym /コース /C# SELF /リフレクションの使い方

リフレクションの使い方

C# SELF
レベル 63, レッスン 1
使用可能

1. はじめに

C# ではどのクラス(あるいは構造体)もメンバーの集合なんだ。代表的なものは次の通り:

  • フィールド (fields): データを保持する変数。
  • プロパティ (properties): get/set を持つ「賢い」ラッパー。
  • メソッド (methods): 機能を実装する。
  • コンストラクター (constructors): インスタンスを作る方法。
  • イベント (events): 賢いパブリッシュ/サブスクライブ。

型の中身を覗いて、値を変えたりできる魔法の虫眼鏡を持っていると想像してみて。これがリフレクションだよ。

名前空間とリフレクションの主要型

始める前に、名前空間を忘れずにインポートしてね:

using System.Reflection;

主要なクラスとインターフェース:

クラス/インターフェース 説明
Type
メインの入り口 — ここから全部始まる
FieldInfo
フィールドに関する情報
PropertyInfo
プロパティに関する情報
MethodInfo
メソッドに関する情報
ConstructorInfo
コンストラクターに関する情報
EventInfo
イベントに関する情報
MemberInfo
上のもの全部の共通の「親」

2. リフレクションで型のメンバーを取得する

フィールドとプロパティを取得する

例: フィールドとプロパティの列挙

例えば、こんなクラスがあるとする:

public class Person
{
    public string Name { get; set; }
    public int Age;
    private string Secret = "秘密の言葉";
}

フィールドを取得:

Type personType = typeof(Person);

FieldInfo[] fields = personType.GetFields(
    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

foreach (var field in fields)
{
    Console.WriteLine($"フィールド: {field.Name}, 型: {field.FieldType.Name}, アクセス: {field.Attributes}");
}

プロパティを取得:

PropertyInfo[] properties = personType.GetProperties(
    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

foreach (var property in properties)
{
    Console.WriteLine($"プロパティ: {property.Name}, 型: {property.PropertyType.Name}");
}

補足

  • BindingFlags はどのメンバーを探すかを指定する「フラグ」の集合(public/private、instance/static 等)。
  • フィールドとプロパティは違うよ: フィールドは生のデータ、プロパティは裏で get/set を呼ぶラッパー。

メソッドを取得する

MethodInfo[] methods = personType.GetMethods(
    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

foreach (var method in methods)
{
    Console.WriteLine($"メソッド: {method.Name}");
}

出力されるメソッドは多くて驚くかも。プロパティのゲッター/セッターや、object 由来のメソッドも混ざるよ。

便利なフィルター: 自分で定義した「明示的な」メソッドだけを取りたいなら、名前や属性でさらにフィルターしよう。

どの BindingFlags を使うか

何を探すか 呼び出し例 説明
公開フィールド
GetFields(BindingFlags.Public | ...)
外部から見えるもの全部
非公開フィールド
GetFields(BindingFlags.NonPublic | ...)
内部に隠れているもの
インスタンスのみ
BindingFlags.Instance
static じゃないものだけ
静的のみ
BindingFlags.Static
static のものだけ
全部まとめて
BindingFlags.Public | NonPublic | Instance | Static
フルリスト

3. 型のインスタンスを動的に作成する

なんで便利なの?
コンパイル時に型が分からなくてもインスタンスを作れるんだ。

簡単な方法 — Activator.CreateInstance

// Type を何らかの方法で取得、例えば:
Type myType = typeof(Person);
// オブジェクトを作成:
object obj = Activator.CreateInstance(myType);
// 今 obj は Person のインスタンスだけど object 型だよ

引数なしコンストラクターがないクラスでもパラメーターを渡せるよ:

public class Animal
{
    public string Name { get; }
    public Animal(string name) { Name = name; }
}

// ...
Type animalType = typeof(Animal);
object dog = Activator.CreateInstance(animalType, "シャーリク");

注意!

Console.WriteLine(dog.GetType().Name); // Animal

もっと細かく — ConstructorInfo を使う

ConstructorInfo ctor = animalType.GetConstructor(new Type[] { typeof(string) });
object dog = ctor.Invoke(new object[] { "シャーリク" });

実務では大抵 Activator で十分だけど、プライベートコンストラクターや正確なシグネチャで作りたいときは ConstructorInfo が役に立つよ。

4. 名前でフィールドやプロパティにアクセスする

フィールドを取得して変更する

Person p = new Person();
Type t = p.GetType();

FieldInfo field = t.GetField("Age");
field.SetValue(p, 42);
Console.WriteLine(field.GetValue(p)); // 42

非公開フィールドを扱う:

FieldInfo secret = t.GetField("Secret", BindingFlags.NonPublic | BindingFlags.Instance);
if (secret != null)
{
    secret.SetValue(p, "おっと、秘密がバレた");
    Console.WriteLine(secret.GetValue(p));
}
else
{
    Console.WriteLine("フィールド Secret が見つかりません");
}

注意! 非公開フィールドへのアクセスを乱用しないで — これはカプセル化の破壊で、バグの原因になり得るよ。

プロパティを取得して変更する

PropertyInfo pi = t.GetProperty("Name");
pi.SetValue(p, "ヴァーシャ");
Console.WriteLine(pi.GetValue(p)); // ヴァーシャ

プロパティはフィールドじゃなくて、裏で動く get/set メソッドだよ。

なぜうまくいかないことがあるの?

フィールドやプロパティが非公開の場合は、必ず適切な BindingFlags(例: NonPublic)を付けて取得してね。プロパティに setter がないなら、リフレクションでも値を書き込めない — そのルールは守られるよ。

5. 名前でメソッドを呼び出す

引数なしの簡単なメソッド呼び出し

public class Greeter
{
    public void SayHello()
    {
        Console.WriteLine("こんにちは!");
    }
}

Greeter g = new Greeter();
Type t = g.GetType();
MethodInfo mi = t.GetMethod("SayHello");
mi.Invoke(g, null); // こんにちは!

引数ありのメソッド呼び出し

public class MathOp
{
    public int Add(int x, int y) => x + y;
}

MathOp calc = new MathOp();
Type t = calc.GetType();
MethodInfo mi = t.GetMethod("Add");
object result = mi.Invoke(calc, new object[] { 7, 5 });
Console.WriteLine(result); // 12

static メソッドを呼ぶときは、Invoke の最初の引数に null を渡してね。

オーバーロードされたメソッドの扱い

MethodInfo mi = t.GetMethod(
    "Add", 
    new Type[] { typeof(int), typeof(int) }
);

あるいは列挙して自分でフィルタする方法もあるよ:

foreach(var method in t.GetMethods()) {
    if (method.Name == "Add" && method.GetParameters().Length == 2) 
    {
        // ... これが対象のメソッド!
    }
}

6. 便利なコツ

型を解析するときによく使うメンバー/メソッド/プロパティ

クラスメンバー 情報取得用メソッド 戻りクラス 読み書き方法
フィールド (field)
GetField, GetFields
FieldInfo
.GetValue(obj), .SetValue(obj, x)
プロパティ (prop)
GetProperty, GetProperties
PropertyInfo
.GetValue(obj), .SetValue(obj, x)
メソッド (method)
GetMethod, GetMethods
MethodInfo
.Invoke(obj, パラメーター)
コンストラクター
GetConstructor, GetConstructors
ConstructorInfo
.Invoke(パラメーター)

実務での使いどころ

  • ORM フレームワーク(例: Entity Framework はモデルから SQL を組み立てる)で使われる。
  • シリアライザやパーサ(例: JSON シリアライザー)で使われる。
  • 汎用的なロギング、テスト、UI ジェネレータ系のライブラリ。
  • 面接で「汎用的なオブジェクトコピーをどう書く?」なんて話題になることがある。
  • プラグインを書くとき、外部コードを読み込んで呼び出す用途。

今は魔法みたいに見えるかもしれないけど、使いこなせるとかなり便利になるよ!

7. よくあるミスと注意点

多くの人が BindingFlags を間違えて、非公開フィールドが見えなかったり、逆に余計なメソッドを全部拾ってきたりする。必ずフラグの組み合わせを確認して: 非公開フィールドなら NonPublic、静的なら Static を付けるんだ。

Invoke でメソッド呼び出すときは、宣言通りの順序と型で引数を渡してね。順序や型が違うと TargetParameterCountExceptionArgumentException が出るよ。戻り値は object として返ってくるから、キャスト(あるいはパターンマッチ)を忘れずに。

パフォーマンスにも注意: リフレクションは直接呼び出すより遅い。ホットパスでは見つけた Type/MethodInfo/PropertyInfo をキャッシュしたり、事前にデリゲートを生成したりするといいよ。

プロパティのアクセス用メソッド(例: get_PropertyNameset_PropertyName)をよく見るけど、これはプロパティのアクセサーだから手動で作る必要はないよ。

非公開フィールドやプロパティを変更するとカプセル化を壊すことになる。間違った使い方は取りにくいバグを生むから注意して。ベンおじさんの言葉を借りれば: 「大きな力には大きな責任が伴う」 — 慎重に使おう。

2
タスク
C# SELF, レベル 63, レッスン 1
ロック未解除
リフレクションを使ったメソッドの呼び出し
リフレクションを使ったメソッドの呼び出し
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION