Як створити спеціальний атрибут у C #


119

Я намагався багато разів, але все ще не в змозі зрозуміти використання спеціальних атрибутів (я вже пройшов безліч посилань).

Чи може хто-небудь пояснити мені дуже базовий приклад спеціального атрибута з кодом?

Відповіді:


96

Хоча код для створення спеціального атрибута досить простий, дуже важливо, щоб ви зрозуміли, що таке атрибути:

Атрибути - це метадані, зібрані у вашій програмі. Самі атрибути не додають ніякого функціоналу до класу, властивості чи модуля - лише дані. Однак, використовуючи рефлексію, можна використовувати ці атрибути для створення функціональності.

Так, наприклад, давайте подивимось на блок валідаційних програм із бібліотеки корпорацій Майкрософт . Якщо ви подивитесь на приклад коду, ви побачите:

    /// <summary>
    /// blah blah code.
    /// </summary>
    [DataMember]
    [StringLengthValidator(8, RangeBoundaryType.Inclusive, 8, RangeBoundaryType.Inclusive, MessageTemplate = "\"{1}\" must always have \"{4}\" characters.")]
    public string Code { get; set; }

З фрагменту вище можна здогадатися, що код завжди буде підтверджений, коли він буде змінений відповідно до правил Валідатора (у прикладі має бути щонайменше 8 символів та максимум 8 символів). Але правда полягає в тому, що Атрибут нічого не робить; як було сказано раніше, вони додають лише метадані до властивості.

Однак у бібліотеці підприємств є Validation.Validateметод, який буде вивчати ваш об’єкт, і для кожного властивості він перевірятиме, чи вміст порушує правило, повідомлене атрибутом.

Отже, саме так слід подумати про атрибути - спосіб додати дані до коду, які згодом можуть бути використані іншими методами / класами / тощо.


мені дуже сподобається відповідь і спеціально ", ще одне питання я можу поставити таку ж умову у встановленому викладі вищевказаного коду, так чим він відрізняється від attributs,
slash shogdhe

1
@slash: Чи можете ви перефразувати це? Я не зовсім зрозумів питання.
Бруно Брант

1
Я думаю, що слэш мав на увазі запитати про різницю між використанням атрибутів та введенням фактичного коду перевірки всередині налаштування властивості. Відповідь: Хоча запис коду всередині сеттера може бути зроблено для перевірки значення, лише використання атрибутів не виконає перевірку як таке. Атрибути - це лише "метадані". Інший код десь ще повинен зацікавити атрибутами, які ви використовуєте, прочитати їх та виконувати дії, засновані на них. Типовим прикладом є бібліотека перевірки, як згадував @BrunoBrant.
romar

10
Не впевнений, чому це прийнята відповідь. Актуальне питання (яке також індексується в Google) - "Як створити спеціальний атрибут у C #". Відповіді зовсім не вникають у цю тему. З другого боку, друга відповідь.
Drakestar

Я думаю, друга відповідь більше пов'язана з питанням.
Мохаммед Тахеріан

267

Ви починаєте з написання класу, який походить від Attribute :

public class MyCustomAttribute: Attribute
{
    public string SomeProperty { get; set; }
}

Тоді ви можете прикрасити що-небудь (клас, метод, властивість, ...) цим атрибутом:

[MyCustomAttribute(SomeProperty = "foo bar")]
public class Foo
{

}

і, нарешті, ви б використали роздуми, щоб отримати його:

var customAttributes = (MyCustomAttribute[])typeof(Foo).GetCustomAttributes(typeof(MyCustomAttribute), true);
if (customAttributes.Length > 0)
{
    var myAttribute = customAttributes[0];
    string value = myAttribute.SomeProperty;
    // TODO: Do something with the value
}

Ви можете обмежити цільові типи, до яких цей спеціальний атрибут може бути застосований за допомогою атрибута AttributeUsage :

/// <summary>
/// This attribute can only be applied to classes
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class MyCustomAttribute : Attribute

Важливі речі, які потрібно знати про атрибути:

  • Атрибути - це метадані.
  • Їх запікають на зборах в компіляції, що має дуже серйозні наслідки того, як ви могли встановити їх властивості. Приймаються лише постійні (відомі під час компіляції) значення
  • Єдиний спосіб зробити будь-який сенс і використовувати власні атрибути - це використовувати Reflection . Тож якщо ви не використовуєте рефлексію під час виконання, щоб отримати їх і прикрасити щось спеціальним атрибутом, не очікуйте, що трапиться багато чого.
  • Час створення атрибутів не детермінований. Вони є ініційованими CLR, і ви не маєте абсолютно ніякого контролю над цим.

3
Де, в якій функції / класі, я буду використовувати "рефлексію для її отримання"
Хасан Юсеф

@Hasan A Yousef, наприклад, в Entity Framework є атрибут "Key", який говорить про фреймворк: Це властивість слід вважати первинним ключем. Створюючи ORM, атрибути дуже корисні
Парса

Як ви отримуєте доступ до спеціального атрибуту для властивості, а не класу?
Полотно

docs.microsoft.com/en-us/dotnet/standard/attributes/… просто для повноти ця msdn-сторінка дуже добре підсумовує її
Barış Akkurt

З дженериками набагато, набагато простіше отримати типи:var value = typeof(Foo).GetCustomAttributes<MyCustomAttribute>().First().SomeProperty;
jpaugh

27

Використовуючи / копіюючи велику відповідь Даріна Димитрова , ось як отримати доступ до користувацького атрибуту для властивості, а не класу:

Оформлена власність [класу Foo]:

[MyCustomAttribute(SomeProperty = "This is a custom property")]
public string MyProperty { get; set; }

Отримання:

PropertyInfo propertyInfo = typeof(Foo).GetProperty(propertyToCheck);
object[] attribute = propertyInfo.GetCustomAttributes(typeof(MyCustomAttribute), true);
if (attribute.Length > 0)
{
    MyCustomAttribute myAttribute = (MyCustomAttribute)attribute[0];
    string propertyValue = myAttribute.SomeProperty;
}

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

foreach (PropertyInfo propertyInfo in Foo.GetType().GetProperties())
{
    string propertyName = propertyInfo.Name;

    object[] attribute = propertyInfo.GetCustomAttributes(typeof(MyCustomAttribute), true);
    // Just in case you have a property without this annotation
    if (attribute.Length > 0)
    {
        MyCustomAttribute myAttribute = (MyCustomAttribute)attribute[0];
        string propertyValue = myAttribute.SomeProperty;
        // TODO: whatever you need with this propertyValue
    }
}

Велике спасибі тобі, Дарин !!


як би ми розширили це, якщо ми не знаємо, які типи атрибутів існують у властивості? object[] attribute = propertyInfo.GetCustomAttributes(typeof(???), true);Я просто хочу переглядати їх і називати метод m1()кожного невідомого атрибута
heyNow

0

Коротка відповідь - це створити атрибут в c #, вам потрібно лише успадкувати його з класу Attribute, просто це :)

Але тут я детально поясню атрибути:

в основному атрибути - це класи, які ми можемо використовувати їх для застосування нашої логіки до збірок, класів, методів, властивостей, полів, ...

У .Net, Microsoft надала такі заздалегідь задані атрибути, як застарілі або атрибути перевірки, такі як ([Потрібно], [StringLength (100)], [Діапазон (0, 999,99)])], також у нас є такі атрибути, як ActionFilters в asp.net що може бути дуже корисним для застосування бажаної логіки до наших кодів (прочитайте цю статтю про фільтри дій, якщо ви захоплені її вивченням)

З іншого боку, ви можете застосувати своєрідну конфігурацію до свого атрибуту через AttibuteUsage.

  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]

Коли ви прикрашаєте клас атрибутів AttributeUsage, ви можете сказати c # компілятору, де я буду використовувати цей атрибут: я збираюся використовувати це на класах, на збірках щодо властивостей або на ..., і мій атрибут дозволяється використовувати кілька разів на визначені цілі (класи, збірки, властивості, ...) чи ні ?!

Після цього визначення щодо атрибутів я покажу вам приклад: Уявіть, що ми хочемо визначити новий урок в університеті, і ми хочемо дозволити просто адміністраторам і магістрам в нашому університеті визначити новий урок, добре?

namespace ConsoleApp1
{
    /// <summary>
    /// All Roles in our scenario
    /// </summary>
    public enum UniversityRoles
    {
        Admin,
        Master,
        Employee,
        Student
    }

    /// <summary>
    /// This attribute will check the Max Length of Properties/fields
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
    public class ValidRoleForAccess : Attribute
    {
        public ValidRoleForAccess(UniversityRoles role)
        {
            Role = role;
        }
        public UniversityRoles Role { get; private set; }

    }


    /// <summary>
    /// we suppose that just admins and masters can define new Lesson
    /// </summary>
    [ValidRoleForAccess(UniversityRoles.Admin)]
    [ValidRoleForAccess(UniversityRoles.Master)]
    public class Lesson
    {
        public Lesson(int id, string name, DateTime startTime, User owner)
        {
            var lessType = typeof(Lesson);
            var validRolesForAccesses = lessType.GetCustomAttributes<ValidRoleForAccess>();

            if (validRolesForAccesses.All(x => x.Role.ToString() != owner.GetType().Name))
            {
                throw new Exception("You are not Allowed to define a new lesson");
            }
            
            Id = id;
            Name = name;
            StartTime = startTime;
            Owner = owner;
        }
        public int Id { get; private set; }
        public string Name { get; private set; }
        public DateTime StartTime { get; private set; }

        /// <summary>
        /// Owner is some one who define the lesson in university website
        /// </summary>
        public User Owner { get; private set; }

    }

    public abstract class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime DateOfBirth { get; set; }
    }


    public class Master : User
    {
        public DateTime HireDate { get; set; }
        public Decimal Salary { get; set; }
        public string Department { get; set; }
    }

    public class Student : User
    {
        public float GPA { get; set; }
    }



    class Program
    {
        static void Main(string[] args)
        {

            #region  exampl1

            var master = new Master()
            {
                Name = "Hamid Hasani",
                Id = 1,
                DateOfBirth = new DateTime(1994, 8, 15),
                Department = "Computer Engineering",
                HireDate = new DateTime(2018, 1, 1),
                Salary = 10000
            };
            var math = new Lesson(1, "Math", DateTime.Today, master);

            #endregion

            #region exampl2
            var student = new Student()
            {
                Name = "Hamid Hasani",
                Id = 1,
                DateOfBirth = new DateTime(1994, 8, 15),
                GPA = 16
            };
            var literature = new Lesson(2, "literature", DateTime.Now.AddDays(7), student);
            #endregion

            ReadLine();
        }
    }


}

У реальному світі програмування, можливо, ми не використовуємо цей підхід для використання атрибутів, і я сказав це через його навчальний момент використання атрибутів

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.