Menu
Coddy logo textTech

Атрибуты и рефлексия

Часть раздела Объектно-ориентированное программирование путешествия по C# на Coddy. Урок 44 из 70.

Атрибуты — это теги метаданных, которые прикрепляются к элементам кода, таким как class, methods или свойства. Они не изменяют непосредственно выполнение кода, но предоставляют информацию, которую можно прочитать во время выполнения. Рефлексия — это механизм, который считывает эти метаданные.

Вы применяете attribute, помещая его в квадратные скобки над целевым элементом:

[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);

Атрибуты и рефлексия лежат в основе многих фреймворков: библиотеки сериализации используют их для управления выводом JSON, фреймворки тестирования — для обнаружения методов тестов, а контейнеры внедрения зависимостей — для автоматического связывания сервисов.

challenge icon

Задание

Легко

Давайте создадим систему обнаружения плагинов, которая использует custom attributes и reflection, чтобы автоматически находить и описывать class, отмеченные как плагины. Это похоже на то, как реальные фреймворки обнаруживают компоненты, test methods или API endpoints во время выполнения.

Вы организуете код в трёх файлах:

  • PluginAttribute.cs: создайте custom attribute с именем PluginAttribute в namespace PluginSystem. Ваш attribute должен:
    • Inherit от Attribute
    • Have два свойства только для чтения: Name (string) и Version (string)
    • Принимать оба значения через constructor
  • Plugins.cs: создайте несколько plugin class в namespace PluginSystem. Некоторые из них должны быть помечены вашим PluginAttribute, а один — нет:
    • ImageProcessor — отмечен с помощью [Plugin("Image Processor", "1.0")]
    • DataExporter — отмечен с помощью [Plugin("Data Exporter", "2.5")]
    • HelperUtility — НЕ отмечен никаким attribute (это просто обычный class)
    Каждый class может быть пустым — нас интересует только обнаружение его metadata с помощью reflection.
  • Program.cs: в основном файле используйте reflection, чтобы обнаружить все class, у которых есть PluginAttribute. Для каждого обнаруженного plugin выведите его информацию. Вам потребуется:
    • Получить все types из current assembly
    • Check каждый type на наличие PluginAttribute
    • Извлечь и отобразить свойства attribute Name и Version

Используйте System.Reflection, чтобы проверять types и получать custom attributes. Метод GetCustomAttribute<T>() возвращает null, если attribute отсутствует, поэтому его можно использовать для фильтрации class, которые действительно являются plugin.

Выводите каждый обнаруженный plugin в следующем формате, по одному в строке:

Plugin: {Name} (v{Version})

Плагины должны выводиться в следующем порядке: сначала ImageProcessor, затем DataExporter. Class HelperUtility не должен появляться в выводе, поскольку у него отсутствует attribute.

Ожидаемый вывод:

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

Это задание демонстрирует, как фреймворки используют attributes в качестве маркеров и reflection для их обнаружения — тот же метод лежит в основе контейнеров dependency injection, библиотек сериализации и testing frameworks!

Попробуйте сами

using System;
using System.Reflection;
using PluginSystem;

class Program
{
    public static void Main(string[] args)
    {
        // TODO: Получить все типы из текущей сборки
        // Подсказка: Используйте Assembly.GetExecutingAssembly().GetTypes()
        
        // TODO: Пройтись по каждому типу и проверить, есть ли у него PluginAttribute
        // Подсказка: Используйте type.GetCustomAttribute<PluginAttribute>()
        
        // TODO: Если атрибут существует, выведите информацию о плагине в формате:
        // Plugin: {Name} (v{Version})
        
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C#