Как стать автором
Обновить

Программируем на C# 8.0. Атрибуты

Блог компании Издательский дом «Питер»
image Привет, Хабр! Обращаем ваше внимание на одну новинку (сдана в типографию), доступную уже сейчас для покупки в электронном виде.

Язык C# существует уже около двух десятилетий. Он неуклонно развивался и в плане возможностей, и в плане размера, но основные характеристики Microsoft всегда сохраняла без изменений. Каждая новая возможность должна идеально вписываться в состав предыдущих, улучшая язык, а не превращая его в несвязный набор различных функций.

Несмотря на то что C# по-прежнему остается довольно простым языком, о нем можно сказать гораздо больше, чем о его первом воплощении. Поскольку охват книги достаточно большой, от читателей ожидается определенный уровень технической подготовки.

Вашему внимаю предлагаю отрывок из книги.

Атрибуты


В .NET можно аннотировать компоненты, типы и их члены с помощью атрибутов. Назначение атрибута — регулировать или изменять поведение платформы, инструмента, компилятора или CLR. Например, в главе 1 я демонстрировал класс, аннотированный атрибутом [TestClass]. Он сообщал инфраструктуре юнит-теста, что класс содержит ряд тестов, которые должны быть выполнены как часть набора тестов.

Атрибуты лишь содержат информацию, но ничего не делают сами по себе. Проведу аналогию с физическим миром. Если вы распечатываете этикетку, содержащую информацию о месте назначения и отслеживании, и прикрепляете ее к посылке, эта этикетка сама по себе не заставит посылку добраться до пункта назначения. Такая этикетка полезна только тогда, когда груз находится в обработке у транспортной компании. Когда компания заберет посылку, этикетка понадобится, чтобы определить, как и куда направить груз. Таким образом, этикетка важна, но ее единственной задачей является предоставление информации, которая требуется какой-либо системе. С атрибутами .NET все так же — они работают только в том случае, если кто-то ожидает их найти. Некоторые атрибуты обрабатываются CLR или компилятором, но таких меньшинство. Большинство же используются платформами, библиотеками, инструментами (например, системами юнит-теста) или вашим собственным кодом.

Применение атрибутов

Во избежание необходимости вводить дополнительный набор понятий в систему типов .NET работает с ними как с экземплярами типов .NET. Для использования в качестве атрибута тип должен быть производным от класса System.Attribute, и это его единственная особенность. Чтобы применить атрибут, вы помещаете имя типа в квадратные скобки и, как правило, размещаете непосредственно перед целью атрибута. Листинг 14.1 показывает некоторые атрибуты из среды тестирования Microsoft. Один я применил к классу, чтобы указать, что он содержит тесты, которые я хотел бы запустить. Кроме этого, я применил атрибуты к отдельным методам, сообщая среде тестирования, какие из них представляют собой тесты, а какие содержат код инициализации, который должен выполняться перед каждым тестом.

Листинг 14.1. Атрибуты в классе юнит-теста

using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ImageManagement.Tests
{
   [TestClass]
   public class WhenPropertiesRetrieved
   {
      private ImageMetadataReader _reader;
      [TestInitialize]
      public void Initialize()
      {
         _reader = new ImageMetadataReader(TestFiles.GetImage());
      }
      [TestMethod]
      public void ReportsCameraMaker()
      {
         Assert.AreEqual(_reader.CameraManufacturer, "Fabrikam");
      }
      [TestMethod]
      public void ReportsCameraModel()
      {
          Assert.AreEqual(_reader.CameraModel, "Fabrikam F450D");
      }
   }
}

Если вы заглянете в документацию по большинству атрибутов, вы обнаружите, что их настоящие имена оканчиваются на Attribute. Если нет класса с именем, указанным в скобках, компилятор C# попытается добавить Attribute, поэтому атрибут [TestClass] в листинге 14.1 ссылается на класс TestClassAttribute. Если хотите, вы можете записывать имя класса полностью, например [TestClassAttribute], но чаще используют более короткую форму.

Если необходимо применить несколько атрибутов, то у вас есть два варианта. Можно предоставить несколько наборов скобок или поместить несколько атрибутов в одну пару скобок, разделив их запятыми.

Некоторые типы атрибутов способны принимать аргументы конструктора. Например, среда тестирования Microsoft содержит атрибут TestCategoryAttribute. При запуске можно выбрать выполнение только тех тестов, которые принадлежат определенной категории. Данный атрибут требует, чтобы вы передали имя категории в качестве аргумента конструктора, потому что нет смысла применять этот атрибут без указания имени. Синтаксис для указания аргументов конструктора атрибута вполне ожидаемый (листинг 14.2).

Листинг 14.2. Атрибут с аргументом конструктора

[TestCategory("Property Handling")]
[TestMethod]
public void ReportsCameraMaker()
{
...

Вы также можете указать свойства или значения полей. Характеристиками некоторых атрибутов можно управлять только через свойства или поля, но не через аргументы конструктора. (Если атрибут имеет множество необязательных настроек, обычно проще представить их как свойства или поля вместо определения перегрузки конструктора для каждой возможной комбинации настроек.) Синтаксис заключается в одной или нескольких записях вида PropertyOrFieldName=Value после аргументов конструктора (или вместо них, если их нет). В листинге 14.3 показан другой атрибут, используемый в юнит-тестировании, ExpectedExceptionAttribute, позволяющий указать, что при выполнении теста вы ожидаете, что он выдаст конкретное исключение. Тип исключения является обязательным, поэтому мы передаем его в качестве аргумента конструктора, но данный атрибут позволяет также указать, должен ли исполнитель теста принимать исключения типа, производного от указанного. (По умолчанию он принимает только точное совпадение.) Это поведение управляется с помощью свойства AllowDerivedTypes.

Листинг 14.3. Указание необязательных настроек атрибута со свойствами

[ExpectedException(typeof(ArgumentException), AllowDerivedTypes = true)]
[TestMethod]
public void ThrowsWhenNameMalformed()
{
...

Применение атрибута не ведет к его созданию. Все, что вы делаете при применении атрибута, — это предоставляете инструкции о том, как атрибут должен быть создан и инициализирован, если он кому-то потребуется. (Бытует распространенное заблуждение, что атрибуты метода создаются при запуске метода. Это не так.) Когда компилятор создает метаданные для сборки, он включает информацию о том, какие атрибуты были применены к каким элементам, включая список аргументов конструктора и значения свойств, а CLR будет извлекать и использовать эту информацию, только если это кому-то понадобится. Например, когда вы говорите Visual Studio запустить ваши юнит-тесты, он загрузит вашу тестовую сборку, а затем для каждого открытого типа запросит у CLR все связанные с тестированием атрибуты. Это и есть та точка, в которой создаются атрибуты. Если просто загрузить сборку, скажем добавив ссылку на нее из другого проекта, а затем использовать некоторые из содержащихся в ней типов, то атрибуты создаваться не будут — они останутся не более чем набором инструкций, затерянных в метаданных вашей сборки.

Цели атрибутов

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

В большинстве случаев вы обозначаете цель, просто помещая атрибут непосредственно перед ней. Но это не сработает в случае сборок или модулей, потому что в вашем исходном коде нет ничего, что бы их представляло, — все в вашем проекте идет в сборку, которую он производит. Модули, в свою очередь, тоже являются совокупностью (как правило, составляя сборку, как я описал в главе 12). Поэтому для них мы должны явно указать цель в начале атрибута. Вы часто будете видеть атрибуты уровня сборки, подобные показанным в листинге 14.4, в файле GlobalSuppressions.cs. Visual Studio иногда предлагает варианты для изменения вашего кода, и если вы решите подавить этот функционал, это можно сделать с помощью атрибутов уровня сборки.

Листинг 14.4. Атрибуты уровня сборки

[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage(
   "StyleCop.CSharp.NamingRules",
   "SA1313:Parameter names should begin with lower-case letter",
   Justification = "Triple underscore acceptable for unused lambda parameter",
   Scope = "member",
   Target = "~M:Idg.Examples.SomeMethod")]

Атрибуты уровня модуля следуют той же схеме, хотя и встречаются гораздо реже. Не в последнюю очередь это происходит потому, что многомодульные сборки встречаются довольно редко и не поддерживаются .NET Core. В листинге 14.5 показано, как настроить возможность отладки конкретного модуля в том случае, если вы хотите, чтобы один модуль в многомодульной сборке был легко отлаживаемым, а остальные — JIT-компилируемыми с полной оптимизацией. (Это специально придуманный сценарий, с помощью которого я могу показать синтаксис. На практике вы вряд ли захотите это делать.) Я расскажу об атрибуте DebuggableAttribute позже, в подразделе «JIT-компиляция» на с. 743.

Листинг 14.5. Атрибут уровня модуля

using System.Diagnostics;
[module: Debuggable(
DebuggableAttribute.DebuggingModes.DisableOptimizations)]

Возвращаемые значения методов могут быть аннотированы, и это также требует квалификации, потому что атрибуты возвращаемого значения располагаются перед методом, там же, где и атрибуты, которые применяются к самому методу. (Атрибуты для параметров не нуждаются в квалификации, потому что они располагаются в круглых скобках вместе с аргументами.) В листинге 14.6 показан метод с атрибутами, применяемыми как к методу, так и к типу возвращаемого значения. (Атрибуты в этом примере являются частью служб взаимодействия, которые позволяют коду .NET вызывать внешний код, такой как API ОС. В этом примере импортируется функция библиотеки Win32, что позволяет использовать ее из C#. Существует несколько различных представлений для логических значений в неуправляемом коде, поэтому в данном случае я аннотировал возвращаемый тип с помощью атрибута MarshalAsAttribute, указав, какой именно тип следует ожидать CLR.)

Оформить предзаказ бумажной книги можно на нашем сайте
Теги:книга
Хабы: Блог компании Издательский дом «Питер»
Всего голосов 8: ↑6 и ↓2+4
Просмотры3.6K
Комментарии Комментарии 5

Похожие публикации

Лучшие публикации за сутки

Информация

Дата основания
Местоположение
Россия
Сайт
piter.com
Численность
201–500 человек
Дата регистрации

Блог на Хабре