Подтвердить что ты не робот

Как работают классы атрибутов?

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

Как создаются классы атрибутов? Являются ли они экземплярами, когда экземпляр класса применяется? Является ли экземпляр экземпляра для каждого класса, созданного для его применения? Например. если я применил класс SerializableAttribute к классу MyData, и я создам экземпляр 5 экземпляров MyData, будет ли 5 ​​экземпляров класса SerializbleAttribute, созданного за кулисами? Или существует только один экземпляр, разделяемый всеми из них?

Как экземпляры класса атрибутов доступа к классу, с которым они связаны? Как класс SerializableAttribute обращается к классу, к которому он применяется, чтобы он мог сериализовать данные? Имеет ли какое-то свойство SerializableAttribute.ThisIsTheInstanceIAmAppliedTo?:) Или это работает в обратном направлении, что всякий раз, когда я сериализую что-то, функция Serialize, через которую я передаю экземпляр MyClass, будет рефлексивно проходить через атрибуты и найти экземпляр SerialiableAttribute?

4b9b3361

Ответ 1

Я не использовал атрибуты в своей повседневной работе раньше, но я читал о них. Также я сделал несколько тестов, чтобы поддержать то, что я скажу здесь. Если я ошибаюсь в любом месте - не стесняйтесь сказать мне это:)

Из того, что я знаю, атрибуты не действуют как обычные классы. Они не создаются при создании объекта, к которому они применяются, а не к одному статическому экземпляру, а не по 1 на каждый экземпляр объекта. Они также не получают доступ к классу, к которому они применяются.

Вместо этого они действуют как свойства (атрибуты?: P) класса. Не похоже на свойства класса .NET, более похожие на свойства свойства "одно свойство стекла - прозрачность". Вы можете проверить, какие атрибуты применяются к классу из отражения, а затем действовать соответственно. Это, по сути, метаданные, которые привязаны к определению класса, а не к объектам этого типа.

Вы можете попытаться получить список атрибутов для класса, метода, свойства и т.д. и т.д. Когда вы получите список этих атрибутов - это то, где они будут созданы. Затем вы можете воздействовать на данные в этих атрибутах.

например. таблицы Linq, свойства имеют атрибуты на них, которые определяют, к какой таблице/столбцу они относятся. Но эти классы не используют эти атрибуты. Вместо этого DataContext проверяет атрибуты этих объектов, когда он преобразует деревья выражений linq в код SQL.

Теперь для некоторых реальных примеров.. Я использовал их в LinqPad, поэтому не беспокойтесь о странном дампе (). Я заменил его на Console.WriteLine, чтобы сделать код более понятным для людей, которые не знают об этом:)

void Main()
{
    Console.WriteLine("before class constructor");
    var test = new TestClass();
    Console.WriteLine("after class constructor");

    var attrs = Attribute.GetCustomAttributes(test.GetType()).Dump();
    foreach(var attr in attrs)
        if (attr is TestClassAttribute)
            Console.WriteLine(attr.ToString());
}

public class TestClassAttribute : Attribute
{
    public TestClassAttribute()
    {
        DefaultDescription = "hello";
        Console.WriteLine("I am here. I'm the attribute constructor!");
    }
    public String CustomDescription {get;set;}
    public String DefaultDescription{get;set;}

    public override String ToString()
    {
        return String.Format("Custom: {0}; Default: {1}", CustomDescription, DefaultDescription);
    }
}

[Serializable]
[TestClass(CustomDescription="custm")]
public class TestClass
{
    public int Foo {get;set;}
}

Результат консоли этого метода:

before class constructor
after class constructor
I am here. I'm the attribute constructor!
Custom: custm; Default: hello

И Attribute.GetCustomAttributes(test.GetType()) возвращает этот массив: (в таблице показаны все доступные столбцы для всех записей. Итак, нет, атрибут Serializable не имеет этих свойств:)) LinqPad Attributes Array

У вас есть еще вопросы? Не стесняйтесь спрашивать!

UPD: Я видел, как вы задавали вопрос: зачем им пользоваться? В качестве примера я расскажу вам о библиотеке XML-RPC.NET. Вы создаете свой класс службы XML-RPC с помощью методов, которые будут представлять методы xml-rpc. Главное сейчас: в XmlRpc имена методов могут иметь некоторые специальные символы, например точки. Таким образом, вы можете использовать метод flexlabs.ProcessTask() xml rpc.

Вы определяете этот класс следующим образом:

[XmlRpcMethod("flexlabs.ProcessTask")]
public int ProcessTask_MyCustomName_BecauseILikeIt();

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

Ответ 2

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

Атрибуты могут применяться практически ко всем аспектам вашего кода. Например, атрибуты могут быть связаны на уровне Ассамблеи, такие как атрибуты AssemblyVersion и AssemblyFileVersion, которые определяют номера версий, связанные с сборкой.

[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Затем атрибут Serializable, например, может быть применен к объявлению типа для обозначения типа поддержки сериализации. Фактически этот атрибут имеет особое значение в CLR и фактически хранится как специальная директива непосредственно по типу в IL, он оптимизирован для хранения в виде битового флага, который может обрабатываться намного эффективнее, есть несколько атрибутов на этот характер, который известен как псевдостандартные атрибуты.

Другие атрибуты могут применяться к методам, свойствам, полям, перечислениям, возвращаемым значениям и т.д. Вы можете получить представление о возможных целях, к которым может применяться атрибут, если посмотреть на эту ссылку http://msdn.microsoft.com/en-us/library/system.attributetargets(VS.90).aspx

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

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

TableMappingAttribute
ColumnMappingAttribute

Что вы можете применить к своим классам, в качестве примера у нас есть класс Person

[TableMapping("People")]
public class Person
{
  [ColumnMapping("fname")]
  public string FirstName {get; set;}

  [ColumnMapping("lname")]
  public string LastName {get; set;}
}

Когда это компилируется, за исключением того факта, что компилятор испускает дополнительные метаданные, определенные пользовательскими атрибутами, на что-то другое влияет. Однако теперь вы можете написать PersistanceManager, который может динамически проверять атрибуты экземпляра класса Person и вставлять данные в таблицу People, сопоставляя данные в свойстве FirstName с столбцом fname и свойством LastName в столбце lname.

Что касается вашего вопроса относительно экземпляров атрибутов, экземпляр атрибута не создается для каждого экземпляра вашего класса. Все экземпляры People будут совместно использовать один и тот же экземпляр атрибутов TableMappingAttribute и ColumnMappingAttributes. Фактически экземпляры атрибутов создаются только тогда, когда вы действительно запрашиваете атрибуты в первый раз.

Ответ 3

Да, они создаются с параметрами, которые вы им даете.

Атрибут не "получает доступ" к классу. Атрибут привязан к списку атрибутов класса/свойства в данных отражения.

[Serializable]
public class MyFancyClass
{ ... }

// Somewhere Else:

public void function()
{
   Type t = typeof(MyFancyClass);
   var attributes = t.GetCustomAttributes(true);

   if (attributes.Count(p => p is SerializableAttribute) > 0)
   {
       // This class is serializable, let do something with it!

   }     
}

Ответ 4

Подумайте об атрибутах post-its, которые привязаны к определениям классов или методов (встроенным в метаданные сборки).

Затем у вас может быть модуль процессор/бегун/инспектор, который принимает эти типы, отражая, ищет эти post-its и обрабатывает их по-разному. Это называется декларативным программированием. Вы объявляете какое-то поведение вместо написания кода для них в типе.

  • Атрибут Serializable для типа объявляет, что он построен для сериализации. XmlSerializer может затем принять объект этого класса и сделать нужным. Вы отмечаете методы, которые должны быть сериализованы/скрыты с правильным post-its.
  • другим примером будет NUnit. Бегун NUnit смотрит на атрибуты [TestFixture] для всех классов, определенных в целевой сборке, для идентификации тестовых классов. Затем он ищет методы, отмеченные атрибутом [Test], чтобы идентифицировать те тесты, которые он затем запускает, и отображает результаты.

Возможно, вам захочется выполнить этот учебник в MSDN, на который в большинстве вопросов ответили вместе с примером в конце. Хотя они могли бы извлечь метод, называемый Audit(Type anyType); вместо дублирования этого кода. Пример "распечатывает информацию", проверяя атрибуты.. но вы можете сделать что-либо в том же духе.

Ответ 5

Если вы обратите внимание на этот загружаемый открытый исходный код LINQ to Active Directory (CodePlex), вам может быть интересен механизм Файл Attributes.cs, где Bart De Smet написал все определения классов атрибутов. Я изучил там атрибуты.

Короче говоря, вы можете специализировать класс Attribute и кодировать некоторые специализированные свойства для ваших нужд.

public class MyOwnAttributeClass : Attribute {
    public MyOwnAttributeClass() {
    }
    public MyOwnAttributeClass(string myName) {
        MyName = myName;
    }
    public string MyName { get; set; }
}

а затем вы можете использовать его везде, где полезен MyOwnAttributeClass. Это может быть либо над определением класса, либо с определением свойства.

[MyOwnAttributeClass("MyCustomerName")]
public class Customer {
    [MyOwnAttributeClass("MyCustomerNameProperty")]
    public string CustomerName { get; set; }
}

Затем вы можете получить это через отражение так:

Attribute[] attributes = typeof(Customer).GetCustomAttribute(typeof(MyOwnAttributeClass));

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

Этот код предоставляется как есть и не может компилироваться. Его цель - дать вам представление о том, как это работает.

В самом деле, вы, как правило, хотите иметь для класса другой класс атрибута, чем для свойства.

Надеюсь, это поможет!

Ответ 6

Не так много времени, чтобы дать вам более полный ответ, но вы можете найти Атрибуты, которые были применены к значению с помощью Reflection. Что касается их создания, вы наследуете от класса атрибута и работаете оттуда - и значения, которые вы поставляете с атрибутом, передаются в конструктор класса атрибута.

Прошло некоторое время, как вы могли бы сказать...

Martin