Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
[SerializableAttribute]
[ObsoleteAttribute("Use another vehicle!")]
public class HorseAndBuggy
{
// ...
}
Имейте в виду, что такая сокращенная система обозначения для атрибутов предлагается только в С#. Ее поддерживают не все языки .NET Core.
Указание параметров конструктора для атрибутов
Обратите внимание, что атрибут [Obsolete] может принимать то, что выглядит как параметр конструктора. Если вы просмотрите формальное определение атрибута [Obsolete], щелкнув на нем правой кнопкой мыши в окне кода и выбрав в контекстном меню пункт Go То Definition (Перейти к определению), то обнаружите, что данный класс на самом деле предоставляет конструктор, принимающий System.String:
public sealed class ObsoleteAttribute : Attribute
{
public ObsoleteAttribute(string message, bool error);
public ObsoleteAttribute(string message);
public ObsoleteAttribute();
public bool IsError { get; }
public string? Message { get; }
}
Важно понимать, что когда вы снабжаете атрибут параметрами конструктора, этот атрибут не размещается в памяти до тех пор, пока к параметрам не будет применена рефлексия со стороны другого типа или внешнего инструмента. Строковые данные, определенные на уровне атрибутов, просто сохраняются внутри сборки в виде блока метаданных.
Атрибут [Obsolete] в действии
Теперь, поскольку класс HorseAndBuggy помечен как устаревший, следующая попытка выделения памяти под его экземпляр:
using System;
using ApplyingAttributes;
Console.WriteLine("Hello World!");
HorseAndBuggy mule = new HorseAndBuggy();
приводит к выдаче компилятором предупреждающего сообщения, а именно — предупреждения CS0618 с сообщением, включающим информацию, которая передавалась атрибуту:
‘HorseAndBuggy’ is obsolete: ‘Use another vehicle!'
HorseAndBuggy устарел: Используйте другое транспортное средство!
Среды Visual Studio и Visual Studio Code оказывают помощь также посредством IntelliSense, получая информацию через рефлексию.
На рис. 17.1 показаны результаты действия атрибута [Obsolete] в Visual Studio, а на рис. 17.2 — в Visual Studio Code. Обратите внимание, что в обеих средах используется термин deprecated вместо obsolete.
В идеальном случае к настоящему моменту вы уже должны понимать перечисленные ниже ключевые моменты, касающиеся атрибутов .NET Core:
• атрибуты представляют собой классы, производные от System.Attribute;
• атрибуты дают в результате встроенные метаданные;
• атрибуты в основном бесполезны до тех пор, пока другой агент не проведет в их отношении рефлексию;
• атрибуты в языке C# применяются с использованием квадратных скобок.
А теперь давайте посмотрим, как реализовывать собственные специальные атрибуты и создавать специальное программное обеспечение, которое выполняет рефлексию по встроенным метаданным.
Построение специальных атрибутов
Первый шаг при построении специального атрибута предусматривает создание нового класса, производного от System.Attribute. Не отклоняясь от автомобильной темы, повсеместно встречающейся в книге, создайте новый проект типа Class Library (Библиотека классов) на C# под названием AttributedCarLibrary. В этой сборке будет определено несколько классов для представления транспортных средств, каждый из которых описан с использованием специального атрибута по имени VehicleDescriptionAttribute:
using System;
// Специальный атрибут.
public sealed class VehicleDescriptionAttribute :Attribute
{
public string Description { get; set; }
public VehicleDescriptionAttribute(string description)
=> Description = description;
public VehicleDescriptionAttribute(){ }
}
Как видите, класс VehicleDescriptionAttribute поддерживает фрагмент строковых данных, которым можно манипулировать с помощью автоматического свойства (Description). Помимо того факта, что данный класс является производным от System.Attribute, ничего примечательного в его определении нет.
На заметку! По причинам, связанным с безопасностью, установившейся практикой в .NET Core считается проектирование всех специальных атрибутов как запечатанных. На самом деле среды Visual Studio и Visual Studio Code предлагают фрагмент кода под названием Attribute, который позволяет сгенерировать в окне редактора кода новый класс, производный от System.Attribute. Для раскрытия любого фрагмента кода необходимо набрать его имя и нажать клавишу <ТаЬ> (один раз в Visual Studio Code и два раза в Visual Studio).
Применение специальных атрибутов
С учетом того, что класс VehicleDescriptionAttribute является производным от System.Attribute, теперь можно аннотировать транспортные средства. В целях тестирования добавьте в новую библиотеку классов следующие файлы классов:
// Motorcycle.cs
namespace AttributedCarLibrary
{
// Назначить описание с помощью "именованного свойства".
[Serializable]
[VehicleDescription(Description = "My rocking Harley")]
// Мой покачивающийся Харли
public class Motorcycle
{
}
// HorseAndBuggy.cs
namespace AttributedCarLibrary
{
[Serializable]
[Obsolete ("Use another vehicle!")]
[VehicleDescription("The old gray mare, she ain't what she used to be...")]
// Старая серая лошадка, она уже не та...
public class HorseAndBuggy
{
}
}
// Winnebago.cs
namespace AttributedCarLibrary
{
[VehicleDescription("A very long, slow, but feature-rich auto")]
// Очень длинный, медленный, но обладающий высокими
// техническими характеристиками автомобиль
public class Winnebago
{
}
}
Синтаксис именованных свойств
Обратите внимание, что классу Motorcycle назначается описание с использованием нового фрагмента синтаксиса, связанного с атрибутами, который называется именованным свойством. В конструкторе первого атрибута [VehicleDescription] лежащие в основе строковые данные устанавливаются с применением свойства Description. Когда внешний агент выполнит рефлексию для этого атрибута, свойству Description будет передано указанное значение (синтаксис именованных свойств разрешен, только если атрибут предоставляет поддерживающее запись свойство .NET Core).
По контрасту для типов HorseAndBuggy и Winnebago синтаксис именованных свойств не используется, а строковые данные просто передаются через специальный конструктор. В любом случае после компиляции сборки AttributedCarLibrary с помощью утилиты ildasm.exe можно просмотреть добавленные описания метаданных. Например, ниже показано встроенное описание класса Winnebago:
// CustomAttribute #1
// -------------------------------------------------------
// CustomAttribute Type: 06000005
// CustomAttributeName: AttributedCarLibrary.VehicleDescriptionAttribute :: instance void .ctor(class System.String)