bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] 属性でコードに意味を与える

[C#] 属性でコードに意味を与える

Jul 8, 2026 C# , .NET bucket-sort

前回は、外部アセンブリを読み込み、実行時に型やメソッドを呼び出す方法を見ました。

今回は 属性 を扱います。
属性は、型、メソッド、プロパティ、アセンブリなどに追加できるメタデータです。

属性を使うと、コードに「追加の意味」を与えられます。
そしてリフレクションでその意味を読み取ることで、フレームワークやアプリケーションの動作を変えられます。

属性とは

属性は、コード要素に付与する説明情報です。

たとえば、古くなったメソッドへ Obsolete 属性を付けると、呼び出し側に警告を出せます。

public class FileImporter
{
    [Obsolete("ImportOld は廃止予定です。Import を使ってください。")]
    public void ImportOld()
    {
    }

    public void Import()
    {
    }
}

ImportOld() を呼び出すと、コンパイル時に警告が表示されます。
属性がコンパイラーや実行時の仕組みに意味を伝えている例です。

属性名の短い書き方

属性クラスの名前は、通常 Attribute で終わります。

public sealed class FeatureAttribute : Attribute
{
}

使うときは、末尾の Attribute を省略できます。

[Feature]
public class CsvImportFeature
{
}

もちろん、正式名で書くこともできます。

[FeatureAttribute]
public class JsonImportFeature
{
}

一般的には短い書き方を使います。

属性にコンストラクター引数を渡す

属性にはコンストラクターを定義できます。

[AttributeUsage(AttributeTargets.Class)]
public sealed class FeatureAttribute : Attribute
{
    public FeatureAttribute(string name)
    {
        Name = name;
    }

    public string Name { get; }
}

使用例です。

[Feature("CSV 取り込み")]
public class CsvImportFeature
{
}

コンストラクター引数は、属性を付けるときに必ず指定する値に向いています。

名前付きプロパティを使う

任意指定の値は、プロパティとして用意できます。

[AttributeUsage(AttributeTargets.Class)]
public sealed class FeatureAttribute : Attribute
{
    public FeatureAttribute(string name)
    {
        Name = name;
    }

    public string Name { get; }
    public string Category { get; set; } = "General";
    public int Order { get; set; }
}

使用例です。

[Feature("CSV 取り込み", Category = "Import", Order = 10)]
public class CsvImportFeature
{
}

必須値はコンストラクター、任意値はプロパティにすると、属性の意味が読みやすくなります。

属性の適用先を制限する

AttributeUsage を使うと、属性をどこに付けられるかを制限できます。

[AttributeUsage(
    AttributeTargets.Class | AttributeTargets.Method,
    AllowMultiple = false,
    Inherited = false)]
public sealed class FeatureAttribute : Attribute
{
    public FeatureAttribute(string name)
    {
        Name = name;
    }

    public string Name { get; }
}

この例では、クラスとメソッドにだけ付けられます。
同じ場所に複数付けることはできず、派生クラスには継承されません。

属性の用途が明確なら、適用先を制限しておくと誤用を防げます。

属性をリフレクションで読む

属性は、リフレクションで読み取れます。

using System.Reflection;

Type type = typeof(CsvImportFeature);

FeatureAttribute? attribute = type
    .GetCustomAttribute<FeatureAttribute>();

if (attribute != null)
{
    Console.WriteLine(attribute.Name);
    Console.WriteLine(attribute.Category);
    Console.WriteLine(attribute.Order);
}

複数取得したい場合は GetCustomAttributes<T>() を使います。

foreach (FeatureAttribute attribute in type.GetCustomAttributes<FeatureAttribute>())
{
    Console.WriteLine(attribute.Name);
}

型を直接参照せずに属性を読む

プラグインでは、アプリ本体が属性型を直接参照できる設計にすることが多いです。
ただし、診断ツールのように「未知の属性も表示したい」場合は、属性型を文字列で見ることもあります。

using System.Reflection;

Type type = typeof(CsvImportFeature);

foreach (CustomAttributeData data in type.CustomAttributes)
{
    Console.WriteLine(data.AttributeType.FullName);

    foreach (CustomAttributeTypedArgument argument in data.ConstructorArguments)
    {
        Console.WriteLine($"  引数: {argument.Value}");
    }

    foreach (CustomAttributeNamedArgument argument in data.NamedArguments)
    {
        Console.WriteLine($"  {argument.MemberName}: {argument.TypedValue.Value}");
    }
}

CustomAttributeData は、属性のインスタンスを作らずにメタデータとして読むときに便利です。

アセンブリに属性を付ける

属性は、型やメソッドだけでなく、アセンブリにも付けられます。

using System.Reflection;

[assembly: AssemblyTitle("Sample Plugin")]
[assembly: AssemblyDescription("CSV import plugin")]

別ファイルにまとめることもできます。

// AssemblyInfo.cs
using System.Reflection;

[assembly: AssemblyCompany("MyCompany")]
[assembly: AssemblyProduct("Plugin Samples")]

SDK スタイルのプロジェクトでは、プロジェクトファイルから指定できる属性も多くあります。

<PropertyGroup>
  <AssemblyTitle>Sample Plugin</AssemblyTitle>
  <Company>MyCompany</Company>
  <Product>Plugin Samples</Product>
</PropertyGroup>

プロジェクトファイルで管理できる情報は、ビルド設定と一緒に置けるため扱いやすいです。

属性で機能を発見する

属性は、拡張機能を発見するためにも使えます。

[AttributeUsage(AttributeTargets.Class)]
public sealed class PluginAttribute : Attribute
{
    public PluginAttribute(string displayName)
    {
        DisplayName = displayName;
    }

    public string DisplayName { get; }
    public string Category { get; set; } = "General";
}
[Plugin("CSV 取り込み", Category = "Import")]
public class CsvImporter
{
    public void Run()
    {
        Console.WriteLine("CSV を取り込みます。");
    }
}

探索側です。

using System.Reflection;

Assembly assembly = typeof(CsvImporter).Assembly;

foreach (Type type in assembly.GetTypes())
{
    PluginAttribute? plugin = type.GetCustomAttribute<PluginAttribute>();

    if (plugin == null)
    {
        continue;
    }

    Console.WriteLine($"{plugin.DisplayName} ({plugin.Category})");
    Console.WriteLine($"  型: {type.FullName}");
}

このパターンを使うと、クラス名ではなく属性に基づいて機能を発見できます。

まとめ

属性は、コードにメタデータを付与する仕組みです。
コンパイラーに意味を伝える場合もあれば、実行時にリフレクションで読み取ってアプリケーションの動作を変える場合もあります。

特に、プラグインや拡張機能を作る場合、属性は「この型は何の機能か」「表示名は何か」「どのカテゴリか」を表現するのに向いています。

次回は、リフレクション、遅延バインディング、属性を組み合わせて、拡張可能なアプリケーションの構造を作ります。

C# .NET 属性 Attribute リフレクション メタデータ
← [C#] 外部アセンブリを読み込み実行時に型を呼び出す [C#] プラグインで拡張できるアプリケーションを設計する →

Related Posts

  • [C#] リフレクションで型の中身を調べる Jul 6, 2026
  • [C#] 型メタデータとリフレクションの基礎を理解する Jul 5, 2026
  • [C#] プラグインで拡張できるアプリケーションを設計する Jul 9, 2026
  • [C#] 外部アセンブリを読み込み実行時に型を呼び出す Jul 7, 2026

Table of Contents

  • 属性とは
  • 属性名の短い書き方
  • 属性にコンストラクター引数を渡す
  • 名前付きプロパティを使う
  • 属性の適用先を制限する
  • 属性をリフレクションで読む
  • 型を直接参照せずに属性を読む
  • アセンブリに属性を付ける
  • 属性で機能を発見する
  • まとめ

Recent Posts

  • [C#] プラグインで拡張できるアプリケーションを設計する Jul 9, 2026
  • [C#] 属性でコードに意味を与える Jul 8, 2026
  • [C#] 外部アセンブリを読み込み実行時に型を呼び出す Jul 7, 2026
  • [C#] リフレクションで型の中身を調べる Jul 6, 2026
  • [C#] 型メタデータとリフレクションの基礎を理解する Jul 5, 2026

Categories

  • C#120
  • .NET119
  • AWS27
  • Laravel16
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Diagnostics1
  • Filament1
  • Git1
  • SQS1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • コレクション
  • PHP
  • セキュリティ
  • MySQL
  • Linux
  • パフォーマンス
  • Apache
  • System.Collections.Generic
  • デリゲート
  • Code Snippet
  • DynamoDB
  • LINQ
  • NoSQL
  • PHP-FPM
  • RDS
  • System.Collections
  • Windows
  • メモリ管理
  • リフレクション
  • DoS
  • Nginx
  • WordPress
  • メタデータ
  • ラムダ式
  • 監視
  • 設計
  • Amazon Linux 2023
  • Delegate
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • LINQ to Objects
  • OPCache
  • Pointer
  • Reflection
  • System.Collections.Specialized
  • Unsafe
  • Webサーバー
  • インターフェース
  • オブジェクト指向
  • クラス設計
  • デザインパターン
  • パターンマッチング
  • ポインター
  • 継承
Powered by Hugo & Explore Theme.