Какие атрибуты в .NET?

206

Что такое атрибуты в .NET, для чего они хороши и как мне создавать свои собственные атрибуты?

Corey
источник

Ответы:

146

Метаданные. Данные о ваших объектах / методах / свойствах.

Например, я мог бы объявить Атрибут с именем: DisplayOrder, чтобы я мог легко контролировать, в каком порядке свойства должны отображаться в пользовательском интерфейсе. Затем я могу добавить его в класс и написать некоторые компоненты GUI, которые извлекают атрибуты и упорядочивают элементы UI соответствующим образом.

public class DisplayWrapper
{
    private UnderlyingClass underlyingObject;

    public DisplayWrapper(UnderlyingClass u)
    {
        underlyingObject = u;
    }

    [DisplayOrder(1)]
    public int SomeInt
    {
        get
        {
            return underlyingObject .SomeInt;
        }
    }

    [DisplayOrder(2)]
    public DateTime SomeDate
    {
        get
        {
            return underlyingObject .SomeDate;
        }
    }
}

Тем самым гарантируя, что SomeInt всегда отображается перед SomeDate при работе с моими пользовательскими компонентами графического интерфейса.

Однако вы увидите, что они чаще всего используются вне среды прямого кодирования. Например, конструктор Windows широко использует их, поэтому он знает, как обращаться с объектами, сделанными на заказ. Использование BrowsableAttribute примерно так:

[Browsable(false)]
public SomeCustomType DontShowThisInTheDesigner
{
    get{/*do something*/}
}

Указывает конструктору не указывать это в доступных свойствах в окне «Свойства», например, во время разработки.

Вы могли бы также использовать их для генерации кода, предварительной компиляции операций (например, пост-Sharp) или во время выполнения таких операций, как Reflection.Emit. Например, вы можете написать немного кода для профилирования, который будет прозрачно упакован при каждом вызове, который ваш код делает, и умножит его. Вы можете «отказаться» от времени с помощью атрибута, который вы помещаете в определенные методы.

public void SomeProfilingMethod(MethodInfo targetMethod, object target, params object[] args)
{
    bool time = true;
    foreach (Attribute a in target.GetCustomAttributes())
    {
        if (a.GetType() is NoTimingAttribute)
        {
            time = false;
            break;
        }
    }
    if (time)
    {
        StopWatch stopWatch = new StopWatch();
        stopWatch.Start();
        targetMethod.Invoke(target, args);
        stopWatch.Stop();
        HandleTimingOutput(targetMethod, stopWatch.Duration);
    }
    else
    {
        targetMethod.Invoke(target, args);
    }
}

Объявить их легко, просто создайте класс, который наследуется от Attribute.

public class DisplayOrderAttribute : Attribute
{
    private int order;

    public DisplayOrderAttribute(int order)
    {
        this.order = order;
    }

    public int Order
    {
        get { return order; }
    }
}

И помните, что когда вы используете атрибут, вы можете опустить суффикс «атрибут», компилятор добавит его для вас.

ПРИМЕЧАНИЕ. Атрибуты сами по себе ничего не делают - должен быть какой-то другой код, который их использует. Иногда этот код был написан для вас, но иногда вы должны написать его самостоятельно. Например, компилятор C # заботится о некоторых, а некоторые фреймворки используют некоторые (например, NUnit ищет [TestFixture] в классе и [Test] в тестовом методе при загрузке сборки).
Поэтому при создании собственного настраиваемого атрибута следует учитывать, что он вообще не повлияет на поведение вашего кода. Вам нужно будет написать другую часть, которая проверяет атрибуты (через отражение) и воздействует на них.

Quibblesome
источник
32
Для чего это стоит, это список всех (встроенных) атрибутов .NET: msdn.microsoft.com/en-us/library/aa311259(VS.71).aspx
wprl
1
Как бы вы использовали свой SomeProfilingMethod в качестве атрибута?
RayLoveless
@RayLoveless - это не атрибут, SomeProfilingMethod - это код инструментария, который ищет атрибуты профилирования. В частности, в примере я дал поиск атрибута «отказаться» (NoTimingAttribute), а не атрибута «отказаться». Идея в том, что это время все.
Ужасно
@Quibblesome, не могли бы вы добавить что-то вроде: «Атрибуты ничего не делают сами по себе - для их использования должен быть какой-то другой код (компилятор заботится о паре, некоторые фреймворки используют). Простое создание атрибута не повлияет на поведение кода - вам нужно написать другую часть, которая проверяет атрибуты (через отражение) и действует на них ". (или я могу сделать это, если ты в порядке). Многие люди ожидают, что атрибуты будут волшебным образом работать, и ни один из ответов здесь не проясняет это. (или просто ссылка на stackoverflow.com/questions/4879521/… который охватывает это)
Алексей Левенков
только если вы перестанете использовать Bing. Неа. J / K Я использую DuckDuckGo в качестве основного, который в основном использует Bing IIRC. :)
Ужасно
36

Многие люди ответили, но никто до сих пор не упомянул об этом ...

Атрибуты интенсивно используются с отражением. Отражение уже довольно медленно.

Это очень полезно маркирование пользовательских атрибутов как sealedклассы , чтобы улучшить их производительность во время выполнения.

Также неплохо подумать, где было бы целесообразно использовать такой атрибут, и присвоить свой атрибут (!), Чтобы указать это через AttributeUsage. Список доступных атрибутов может вас удивить:

  • сборочный
  • модуль
  • Класс
  • Struct
  • Enum
  • Конструктор
  • метод
  • Свойство
  • поле
  • Мероприятие
  • Интерфейс
  • параметр
  • делегат
  • ReturnValue
  • GenericParameter
  • Все

Также здорово, что атрибут AttributeUsage является частью подписи атрибута AttributeUsage. Ого за круговые зависимости!

[AttributeUsageAttribute(AttributeTargets.Class, Inherited = true)]
public sealed class AttributeUsageAttribute : Attribute
Дрю Ноакс
источник
13

Атрибуты являются своего рода метаданными для маркировки классов. Это часто используется в WinForms, например, чтобы скрыть элементы управления от панели инструментов, но может быть реализовано в вашем собственном приложении, чтобы позволить экземплярам разных классов вести себя особым образом.

Начните с создания атрибута:

[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true)]
public class SortOrderAttribute : Attribute
{
    public int SortOrder { get; set; }

    public SortOrderAttribute(int sortOrder)
    {
        this.SortOrder = sortOrder;
    }
}

Все классы атрибутов должны иметь суффикс «Атрибут», чтобы быть действительным.
После этого создайте класс, который использует атрибут.

[SortOrder(23)]
public class MyClass
{
    public MyClass()
    {
    }
}

Теперь вы можете проверить определенный класс SortOrderAttribute(если он есть), выполнив следующие действия:

public class MyInvestigatorClass
{
    public void InvestigateTheAttribute()
    {
        // Get the type object for the class that is using
        // the attribute.
        Type type = typeof(MyClass);

        // Get all custom attributes for the type.
        object[] attributes = type.GetCustomAttributes(
            typeof(SortOrderAttribute), true);

        // Now let's make sure that we got at least one attribute.
        if (attributes != null && attributes.Length > 0)
        {
            // Get the first attribute in the list of custom attributes
            // that is of the type "SortOrderAttribute". This should only
            // be one since we said "AllowMultiple=false".
            SortOrderAttribute attribute = 
                attributes[0] as SortOrderAttribute;

            // Now we can get the sort order for the class "MyClass".
            int sortOrder = attribute.SortOrder;
        }
    }
}

Если вы хотите узнать больше об этом, вы всегда можете проверить MSDN, который имеет довольно хорошее описание.
Я надеюсь, что это помогло вам!

Патрик Свенссон
источник
5

Атрибут - это класс, который содержит некоторую функциональность, которую вы можете применить к объектам в вашем коде. Чтобы создать его, создайте класс, который наследуется от System.Attribute.

Что касается того, для чего они хороши ... их использование практически безгранично.

http://www.codeproject.com/KB/cs/dotnetattributes.aspx

TheSmurf
источник
1
«функциональность» здесь не то слово; это метаданные, а не функциональность
Марк Грэвелл
5

Атрибуты похожи на метаданные, применяемые к классам, методам или сборкам.

Они хороши для любого количества вещей (визуализация отладчика, пометка вещей как устаревших, пометка вещей как сериализуемых, список бесконечен).

Создавать свои собственные так же просто, как пирог. Начало здесь:

http://msdn.microsoft.com/en-us/library/sw480ze8(VS.71).aspx

Stu
источник
5

В проекте, над которым я сейчас работаю, есть набор объектов пользовательского интерфейса различных типов и редактор для сборки этих объектов для создания страниц для использования в основном приложении, немного похоже на дизайнер форм в DevStudio. Эти объекты существуют в их собственной сборке, и каждый объект является классом, производным от него, UserControlи имеет собственный атрибут. Этот атрибут определяется так:

[AttributeUsage (AttributeTargets::Class)]
public ref class ControlDescriptionAttribute : Attribute
{
public:
  ControlDescriptionAttribute (String ^name, String ^description) :
    _name (name),
    _description (description)
  {
  }

  property String ^Name
  {
    String ^get () { return _name; }
  }

  property String ^Description
  {
    String ^get () { return _description; }
  }

private:
  String
    ^ _name,
    ^ _description;
};

и я применяю это к классу как это:

[ControlDescription ("Pie Chart", "Displays a pie chart")]
public ref class PieControl sealed : UserControl
{
  // stuff
};

о чем говорили предыдущие постеры.

Для использования атрибута в редакторе есть Generic::List <Type>типы элементов управления. Существует список, из которого пользователь может перетащить и перетащить на страницу, чтобы создать экземпляр элемента управления. Чтобы заполнить список, я получаю ControlDescriptionAttributeэлемент управления и заполняю запись в списке:

// done for each control type
array <Object ^>
  // get all the custom attributes
  ^attributes = controltype->GetCustomAttributes (true);

Type
  // this is the one we're interested in
  ^attributetype = ECMMainPageDisplay::ControlDescriptionAttribute::typeid;

// iterate over the custom attributes
for each (Object ^attribute in attributes)
{
  if (attributetype->IsInstanceOfType (attribute))
  {
    ECMMainPageDisplay::ControlDescriptionAttribute
      ^description = safe_cast <ECMMainPageDisplay::ControlDescriptionAttribute ^> (attribute);

    // get the name and description and create an entry in the list
    ListViewItem
      ^item = gcnew ListViewItem (description->Name);

    item->Tag = controltype->Name;
    item->SubItems->Add (description->Description);

    mcontrols->Items->Add (item);
    break;
  }
}

Примечание: выше приведен C ++ / CLI, но его нетрудно преобразовать в C # (да, я знаю, C ++ / CLI - мерзость, но это то, с чем я должен работать :-()

Вы можете поставить атрибуты на большинство вещей, и есть целый ряд предопределенных атрибутов. Вышеупомянутый редактор также ищет настраиваемые атрибуты свойств, которые описывают свойство и способы его редактирования.

Как только вы поймете всю идею, вы удивитесь, как раньше жили без них.

Skizz
источник
4

Как уже говорилось, Атрибуты относительно легко создавать. Другая часть работы - создание кода, который его использует. В большинстве случаев вы будете использовать отражение во время выполнения, чтобы изменить поведение в зависимости от наличия атрибута или его свойств. Существуют также сценарии, в которых вы будете проверять атрибуты в скомпилированном коде для выполнения статического анализа. Например, параметры могут быть помечены как ненулевые, и инструмент анализа может использовать это как подсказку.

Использование атрибутов и знание соответствующих сценариев для их использования - основная часть работы.

Денис Филлипс
источник
3

Атрибуты - это, по сути, биты данных, которые вы хотите присоединить к вашим типам (классы, методы, события, перечисления и т. Д.)

Идея состоит в том, что во время выполнения какой-либо другой тип / инфраструктура / инструмент будет запрашивать у вашего типа информацию в атрибуте и действовать в соответствии с ним.

Так, например, Visual Studio может запросить атрибуты стороннего элемента управления, чтобы выяснить, какие свойства элемента управления должны отображаться на панели «Свойства» во время разработки.

Атрибуты также можно использовать в аспектно-ориентированном программировании для внедрения / манипулирования объектами во время выполнения на основе атрибутов, которые их украшают, и добавления проверки, ведения журналов и т. Д. К объектам без влияния на бизнес-логику объекта.

urini
источник
2

Вы можете использовать пользовательские атрибуты как простой способ определения значений тегов в подклассах без необходимости писать один и тот же код снова и снова для каждого подкласса. Я натолкнулся на хороший лаконичный пример Джона Уотерса о том, как определять и использовать пользовательские атрибуты в вашем собственном коде.

Существует учебное пособие по адресу http://msdn.microsoft.com/en-us/library/aa288454(VS.71).aspx

Крис Миллер
источник
2

Чтобы начать создавать атрибут, откройте исходный файл C #, введите attributeи нажмите [TAB]. Это расширится до шаблона для нового атрибута.

Джей Базузи
источник
6
Как это отвечает на вопрос? это должен быть комментарий, а не ответ.
Гдорон поддерживает Монику
1

Атрибуты также обычно используются для аспектно-ориентированного программирования. Для примера этого проверьте проект PostSharp .

Джош Дж
источник