Как создать собственный атрибут в C #

119

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

Может ли кто-нибудь объяснить мне очень простой пример настраиваемого атрибута с кодом?

слэш шогдхе
источник

Ответы:

96

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

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

Так, например, давайте посмотрим на блок приложения проверки из корпоративной библиотеки Microsoft . Если вы посмотрите на пример кода, вы увидите:

    /// <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метод, который будет изучать ваш объект, и для каждого свойства он проверяет, не нарушает ли содержимое правило, указанное атрибутом.

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

Бруно Брант
источник
мне действительно понравится ответ, и особенно? », еще один вопрос, я могу поставить такое же условие в формулировку набора приведенного выше кода, так как это отличается от атрибутов,
slash shogdhe
1
@slash: Вы можете перефразировать это? Я не совсем понял вопрос.
Бруно Брант,
1
Я думаю, косая черта предназначена для того, чтобы спросить о разнице между использованием атрибутов и помещением фактического кода проверки в средство установки свойств. Ответ: Хотя для проверки значения можно написать код внутри установщика, использование только атрибутов не будет выполнять проверку как таковую. Атрибуты - это просто «метаданные». Другой код в другом месте должен интересоваться используемыми вами атрибутами, читать их и выполнять действия на их основе. Типичным примером является библиотека проверки, как упоминалось в @BrunoBrant.
ромар
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 Юсеф, например, в Entity Framework есть атрибут «Key», который говорит framework: это свойство следует рассматривать как первичный ключ. При создании ORM очень
Парса,
Как получить доступ к настраиваемому атрибуту свойства, а не класса?
Холст
docs.microsoft.com/en-us/dotnet/standard/attributes/… просто для полноты, на этой странице
msdn
С дженериками получить типы намного, намного проще: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

Короткий ответ - для создания атрибута в С # вам нужно только наследовать его от класса Attribute, только это :)

Но здесь я собираюсь подробно объяснить атрибуты:

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

В .Net Microsoft предоставила некоторые предопределенные атрибуты, такие как устаревшие или атрибуты проверки, такие как ([Required], [StringLength (100)], [Range (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();
        }
    }


}

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

Хамид
источник