Menu
Coddy logo textTech

属性とリフレクション

CoddyのC#ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 44/70。

属性は、クラス、メソッド、プロパティなどのコード要素に付加するメタデータタグです。属性はコードの実行方法を直接変更するものではありませんが、実行時に読み取れる情報を提供します。リフレクションは、このメタデータを読み取る仕組みです。

対象の要素の上に角括弧で囲んで記述することで、属性を適用します。

[Obsolete("Use NewMethod instead")]
public void OldMethod() { }

[Serializable]
public class Person
{
    public string Name { get; set; }
}

Attributeを継承することでカスタム属性を作成できます。

public class AuthorAttribute : Attribute
{
    public string Name { get; }
    public AuthorAttribute(string name) => Name = name;
}

[Author("John")]
public class Calculator { }

リフレクションを使うと、System.Reflection名前空間を使用して、実行時に型を調べたり属性を読み取ったりできます。

Type type = typeof(Calculator);
var attr = type.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine(attr.Name);  // John

メソッドやプロパティを見つけて、それらを動的に呼び出すこともできます。

MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
    Console.WriteLine(method.Name);

Attribute とリフレクションは多くのフレームワークを支えています。シリアライゼーションライブラリはこれらを使用して JSON の出力を制御し、テストフレームワークはこれらを使用してテスト methods を発見し、依存性注入コンテナーはこれらを使用してサービスを自動的に接続します。

challenge icon

チャレンジ

簡単

custom attributes と reflection を使用して、plugin としてマークされたクラスを自動的に見つけて説明する plugin discovery system を構築しましょう。これは、実際の framework が runtime で component、test method、または API endpoint を発見する方法に似ています。

コードを 3 つのファイルに分けて整理します。

  • PluginAttribute.cs: PluginSystem namespace に custom attribute called PluginAttribute を作成します。attribute は次の条件を満たす必要があります。
    • Attribute から Inherit する
    • 2 つの read-only property を Have する: Name (string) と Version (string)
    • constructor を通じて both の値を Accept する
  • Plugins.cs: PluginSystem namespace に複数の plugin class を作成します。いくつかには PluginAttribute を付け、1 つには付けないようにします。
    • ImageProcessor - [Plugin("Image Processor", "1.0")] でマーク
    • DataExporter - [Plugin("Data Exporter", "2.5")] でマーク
    • HelperUtility - いずれの attribute でもマークしない(これは単なる通常の class です)
    各 class は空でも構いません - ここで関心があるのは reflection によってその metadata を発見することだけです。
  • Program.cs: main file で reflection を使用し、PluginAttribute を Have するすべての class を発見します。発見した各 plugin の情報を表示します。次の処理が必要です。
    • current assembly からすべての type を取得する
    • each type について PluginAttribute を check する
    • attribute の Name および Version property を抽出して表示する

System.Reflection を使用して type を inspect し、custom attribute を取得します。GetCustomAttribute<T>() method は attribute が存在しない場合に null を返すため、これを使用して実際の plugin である class を filter できます。

発見した各 plugin を、1 行に 1 つ、次の format で表示します。

Plugin: {Name} (v{Version})

plugin は次の順序で表示してください。最初に ImageProcessor、次に DataExporter です。HelperUtility class には attribute がないため、output に表示されないようにします。

Expected output:

Plugin: Image Processor (v1.0)
Plugin: Data Exporter (v2.5)

この challenge では、framework が attribute を marker として使用し、reflection によってそれらを発見する方法を示します - dependency injection container、serialization library、testing framework を支えているのも同じ technique です。

REQUIRED OUTPUT FORMAT: [Your translated content here]

自分で試してみよう

using System;
using System.Reflection;
using PluginSystem;

class Program
{
    public static void Main(string[] args)
    {
        // TODO: 現在のアセンブリからすべての型を取得する
        // Hint: Assembly.GetExecutingAssembly().GetTypes() を使用する
        
        // TODO: 各型をループして PluginAttribute を持っているか確認する
        // Hint: type.GetCustomAttribute<PluginAttribute>() を使用する
        
        // TODO: 属性が存在する場合、次の形式でプラグイン情報を出力する:
        // Plugin: {Name} (v{Version})
        
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: C#オンラインコンパイラ