Встроеные атрибуты

66

Атрибут Serializable

Чтобы посмотреть, как применение атрибутов в C# выглядит на практике, создадим новое консольное приложение. Предположим, что требуется создать класс по имени Motorcycle (мотоцикл), который бы мог сохраняться в двоичном формате. Для этого достаточно применить к определению этого класса атрибут [Serializable]. Если в классе есть какое-то поле, которое не должно сохраняться, к нему должен быть применен атрибут [NonSerialized]:

// Этот класс может сохраняться на диске.
[Serializable]
public class Motorcycle
{
   // Это поле сохраняться не будет
   [NonSerialized]
   float weightOfСurrentPassengers;
   
  // Эти поля будут сериализованы.
  bool hasRadioSystem;
  bool hasHeadSet;
  bool hasSissyBar;
  ...
  

Действие любого атрибута распространяется только на элемент, который следует сразу же после него. Например, в классе Motorcycle сериализации не будет подвергнуто только поле weightOfCurrentPassengers. Все остальные поля будут обязательно сериализоваться, потому что класс снабжен атрибутом [Serializable].

На данный момент вдаваться в сам процесс сериализации объектов не стоит. Главное обратить внимание на то, что для применения атрибута его имя должно быть заключено в квадратные скобки.

После компиляции этого класса можно просмотреть дополнительные метаданные с помощью утилиты ildasm.ехе. При этом следует обратить внимание на то, что эти атрибуты будут сопровождаться маркером serializable (в строке с треугольником красного цвета внутри класса Motorcycle) и маркером notserialized (в строке, представляющей поле weightOfCurrentPassengers), как показано на рисунке:

Просмотр атрибутов в ildasm.exe

Как не трудно догадаться, к каждому элементу может применяться сразу несколько атрибутов.

Атрибут AttributeUsage

Атрибут AttributeUsage определяет типы элементов, к которым может быть применен объявляемый атрибут. AttributeUsage — это, по существу, еще одно наименование класса System.AttributeUsageAttribute. У него имеется следующий конструктор:

AttributeUsage(AttributeTargets validOn)

где validOn обозначает один или несколько элементов, к которым может быть применен объявляемый атрибут, тогда как AttributeTargets — перечисление, в котором определяются приведенные ниже значения:

All, Assembly, Class, Constructor, Delegate, Enum,
Event, Field, GenericParameter, Interface, Method, 
Module, Parameter, Property, ReturnValue, Struct

Два этих значения или более можно объединить с помощью логической операции ИЛИ. Например, для указания атрибута, применяемого только к полям и свойствам, используются следующие значения:

AttributeTargets.Field | AttributeTargets.Property

В классе атрибута AttributeUsage поддерживаются два именованных параметра. Первым из них является параметр AllowMultiple, принимающий логическое значение. Если это значение истинно, то атрибут может быть применен к одному и тому же элементу неоднократно. Второй именованный параметр Inherited, также принимает логическое значение. Если это значение истинно, то атрибут наследуется производными классами, а иначе он не наследуется. По умолчанию параметр AllowMultiple принимает ложное значение (false), а параметр Inherited — истинное значение (true).

В классе атрибута AttributeUsage определяется также доступное только для чтения свойство ValidOn. Оно возвращает значение типа AttributeTargets, определяющее типы элементов, к которым можно применять объявляемый атрибут. По умолчанию используется значение AttributeTargets.All.

Атрибут Conditional

Атрибут Conditional представляет, вероятно, наибольший интерес среди всех встроенных атрибутов. Ведь он позволяет создавать условные методы, которые вызываются только в том случае, если с помощью директивы #define определен конкретный идентификатор, а иначе метод пропускается. Следовательно, условный метод служит альтернативой условной компиляции по директиве #if.

Conditional — это, по существу, еще одно наименование класса System.Diagnostics.ConditionalAttribute. Для применения атрибута Conditional в исходный код программы следует включить пространство имен System.Diagnostics.

Рассмотрим применение данного атрибута на следующем примере программы:

#define Use

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Motorcycle
    {
        [Conditional("Use")]
        public void UseMotorcycle()
        {
            Console.WriteLine("Доступен");
        }

        [Conditional("NoUse")]
        public void NoUseMotorcycle()
        {
            Console.WriteLine("Не доступен");
        }
    }
    
    class Program
    {
        static void Main()
        {
            Motorcycle moto = new Motorcycle();
            moto.UseMotorcycle();
            moto.NoUseMotorcycle();
            Console.ReadLine();
        }
    }
}

Как видно в данном примере, с помощью атрибута Conditional проверяется наличие различных идентификаторов препроцессора.

Атрибут Conditional можно также применить в классе атрибута, т.е. в классе, наследующем от класса Attribute. Так, если идентификатор определен, то атрибут применяется, когда он встречается в ходе компиляции. В противном случае он не применяется.

На условные методы накладывается ряд ограничений. Во-первых, они должны возвращать значение типа void, а по существу, ничего не возвращать. Во-вторых, они должны быть членами класса или структуры, а не интерфейса. И в-третьих, они не могут предшествовать ключевому слову override.

Атрибут Obsolete

Атрибут Obsolete (сокращенное наименование класса System.ObsoleteAttribute) позволяет пометить элемент программы как устаревший. Ниже приведена общая форма этого атрибута:

[Obsolete("сообщение")]

где сообщение выводится при компилировании элемента программы, помеченного как устаревший. Ниже приведен краткий пример применения данного атрибута:

using System;

namespace ConsoleApplication1
{
    class Program
    {
        [Obsolete("Устаревший метод, лучше использовать что-нибудь поновее :)")]
        static void SomeMethod()
        { }

        static void Main()
        {
            SomeMethod();
        }
    }
}

Во время компиляции будет отображаться предупреждающее сообщение:

Использование атрибута obsolete
Лучший чат для C# программистов