今回から数回に分けて、型メタデータ、リフレクション、属性、遅延バインディング、dynamic、そしてプラグイン構造を扱います。
これらは単なる文法知識ではありません。
実行時に型を調べる、外部 DLL を読み込む、属性で機能を発見する、プラグインを追加する、といった 拡張性の高いアプリケーション を作るための土台になります。
まずこの記事では、型メタデータと System.Type の基本を見ていきます。
型メタデータが必要な理由
.NET のアセンブリには、実行コードだけでなく、型に関する情報が含まれています。
たとえば、次のような情報です。
- 型の名前
- 名前空間
- 基底クラス
- 実装しているインターフェイス
- フィールド
- プロパティ
- メソッド
- コンストラクター
- 属性
- 定義元アセンブリ
このような情報を 型メタデータ と呼びます。
型メタデータがあることで、実行時に「このオブジェクトは何者か」「どんなメソッドを持つか」「どの属性が付いているか」を調べられます。
この仕組みを利用するのがリフレクションです。
サンプル用の型
この記事では、次のような簡単な型を使います。
namespace ReflectionSamples;
public enum EngineState
{
Stopped,
Running,
Broken
}
public class Car
{
public string Name { get; set; }
public int CurrentSpeed { get; private set; }
public EngineState State { get; private set; }
public Car(string name)
{
Name = name;
State = EngineState.Stopped;
}
public void Start()
{
State = EngineState.Running;
}
public void Accelerate(int delta)
{
CurrentSpeed += delta;
}
}
このような普通の型でも、ビルド後のアセンブリには型名、プロパティ、メソッド、列挙値などの情報が保存されています。
オブジェクトから型情報を取得する
実行中のオブジェクトから型情報を取得するには、GetType() を使います。
using ReflectionSamples;
Car car = new Car("Mini");
Type type = car.GetType();
Console.WriteLine(type.FullName);
Console.WriteLine(type.Name);
Console.WriteLine(type.Namespace);
Console.WriteLine(type.BaseType?.FullName);
GetType() は、実際のオブジェクトの型を返します。
変数の宣言型ではなく、実体の型を調べたいときに便利です。
object value = new Car("Mini");
Console.WriteLine(value.GetType().FullName);
この場合、変数の型は object ですが、実体は Car なので Car の型情報が得られます。
コンパイル時に型情報を取得する
型名がコード上で分かっている場合は、typeof を使います。
using ReflectionSamples;
Type carType = typeof(Car);
Type enumType = typeof(EngineState);
Console.WriteLine(carType.FullName);
Console.WriteLine(enumType.FullName);
GetType() はオブジェクトから型を取得します。
typeof は型そのものから型情報を取得します。
Car car = new Car("Mini");
Type a = car.GetType();
Type b = typeof(Car);
Console.WriteLine(a == b);
この例では、どちらも同じ型情報を指すため True になります。
文字列から型情報を取得する
型名を文字列として持っている場合は、Type.GetType() を使えます。
Type? type = Type.GetType("System.String");
if (type != null)
{
Console.WriteLine(type.FullName);
}
自作型の場合は、アセンブリ名まで必要になることがあります。
Type? type = Type.GetType(
"ReflectionSamples.Car, MyApp");
Console.WriteLine(type?.FullName ?? "型が見つかりません。");
文字列から型を探す処理は、設定ファイルで型名を指定したい場合や、プラグイン機構を作る場合に使われます。
ただし、文字列はタイプミスに弱いため、可能な場所では typeof や共通インターフェイスを使う方が安全です。
定義元アセンブリを調べる
型情報から、その型がどのアセンブリに定義されているかを調べられます。
using System.Reflection;
using ReflectionSamples;
Type type = typeof(Car);
Assembly assembly = type.Assembly;
Console.WriteLine(assembly.FullName);
Console.WriteLine(assembly.Location);
プラグインやライブラリを扱うとき、「この型はどの DLL から来たのか」を調べる場面があります。
type.Assembly はその入口になります。
参照しているアセンブリを調べる
アセンブリが参照している別のアセンブリも取得できます。
using System.Reflection;
using ReflectionSamples;
Assembly assembly = typeof(Car).Assembly;
foreach (AssemblyName name in assembly.GetReferencedAssemblies())
{
Console.WriteLine(name.FullName);
}
依存関係の把握、診断ログ、プラグインの互換性チェックなどで役立ちます。
文字列リテラルを調べるという考え方
アセンブリには、コード内で使われる文字列リテラルも含まれます。
通常の C# コードから「すべての文字列リテラル」を簡単に列挙する API はありませんが、逆コンパイルツールやメタデータ閲覧ツールを使うと、アセンブリ内の情報を確認できます。
この事実は、セキュリティ面でも重要です。
API キー、パスワード、接続文字列などをコードへ直接書くと、ビルド後のファイルから読み取られる可能性があります。
// 避けたい例
string apiKey = "hard-coded-secret";
秘密情報は、環境変数、ユーザーシークレット、クラウドの秘密情報管理サービスなどへ逃がすのが基本です。
型情報を見るための小さな関数
最後に、型情報の要約を表示する関数を作ってみます。
using System.Reflection;
static void PrintTypeSummary(Type type)
{
Console.WriteLine($"型名: {type.FullName}");
Console.WriteLine($"基底型: {type.BaseType?.FullName}");
Console.WriteLine($"アセンブリ: {type.Assembly.GetName().Name}");
Console.WriteLine($"public 型: {type.IsPublic}");
Console.WriteLine($"抽象型: {type.IsAbstract}");
Console.WriteLine($"ジェネリック型: {type.IsGenericType}");
Console.WriteLine("メソッド:");
foreach (MethodInfo method in type.GetMethods(
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.DeclaredOnly))
{
Console.WriteLine($" {method.Name}");
}
}
使い方です。
PrintTypeSummary(typeof(string));
PrintTypeSummary(typeof(List<int>));
このように、リフレクションを使うと、型に関する情報を実行時に調べられます。
まとめ
型メタデータは、.NET アセンブリに含まれる型情報です。
System.Type を使うと、型名、メンバー、定義元アセンブリ、属性などを実行時に調べられます。
この仕組みは、DI コンテナー、テストフレームワーク、シリアライザー、プラグイン機構など、多くの拡張性の土台になっています。
次回は、メソッド、フィールド、プロパティ、インターフェイス、ジェネリック型などを詳しく調べる、実践的なメタデータビューアを作ります。