Como criar um atributo personalizado em C #

119

Eu tentei várias vezes, mas ainda não consigo entender o uso de atributos personalizados (já passei por muitos links).

Alguém pode me explicar um exemplo muito básico de um atributo personalizado com código?

slash shogdhe
fonte

Respostas:

96

Embora o código para criar um atributo personalizado seja bastante simples, é muito importante que você entenda o que são os atributos:

Atributos são metadados compilados em seu programa. Os atributos em si não adicionam nenhuma funcionalidade a uma classe, propriedade ou módulo - apenas dados. No entanto, usando reflexão, pode-se aproveitar esses atributos para criar funcionalidade.

Então, por exemplo, vamos dar uma olhada no Bloco de Aplicativos de Validação , da Biblioteca Corporativa da Microsoft . Se você olhar um exemplo de código, verá:

    /// <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; }

Pelo trecho acima, pode-se supor que o código sempre será validado, sempre que alterado, de acordo com as regras do Validador (no exemplo, ter no mínimo 8 caracteres e no máximo 8 caracteres). Mas a verdade é que o Atributo não faz nada; conforme mencionado anteriormente, ele apenas adiciona metadados à propriedade.

Porém, a Biblioteca Corporativa possui um Validation.Validatemétodo que examinará seu objeto e, para cada propriedade, verificará se o conteúdo viola a regra informada pelo atributo.

Então, é assim que você deve pensar sobre atributos - uma forma de adicionar dados ao seu código que podem ser usados ​​posteriormente por outros métodos / classes / etc.

Bruno Brant
fonte
Eu realmente gosto da resposta e especialmente ", mais uma pergunta que posso colocar na mesma condição na instrução set do código acima, então como é diferente dos atributos,
slash shogdhe
1
@slash: Você pode reformular isso? Eu não entendi muito bem a pergunta.
Bruno Brant
1
Acho que slash queria perguntar sobre a diferença entre usar atributos e colocar o código de validação real dentro do configurador de propriedade. Resposta: Embora a escrita de código dentro do configurador possa ser feita para validar o valor, o uso de atributos por si só não realizará a validação como tal. Atributos são apenas "metadados". Outro código em outro lugar deve estar interessado nos atributos que você usa, lê-os e executa ações com base neles. Um exemplo típico é uma biblioteca de validação, como @BrunoBrant mencionou.
romar
10
Não sei por que essa é a resposta aceita. A questão real (que também é indexada no Google) é "Como criar um atributo personalizado em C #". As respostas não se aprofundam nesse tópico. A 2ª resposta sim, por outro lado.
Drakestar
Acho que a segunda resposta está mais relacionada à pergunta.
Mohammad Taherian
267

Você começa escrevendo uma classe que deriva de Attribute :

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

Então você pode decorar qualquer coisa (classe, método, propriedade, ...) com este atributo:

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

}

e, finalmente, você usaria reflexão para buscá-lo:

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
}

Você pode limitar os tipos de destino aos quais este atributo personalizado pode ser aplicado usando o atributo AttributeUsage :

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

Coisas importantes a saber sobre os atributos:

  • Atributos são metadados.
  • Eles são incorporados ao assembly em tempo de compilação, o que tem implicações muito sérias de como você pode definir suas propriedades. Apenas valores constantes (conhecidos em tempo de compilação) são aceitos
  • A única maneira de fazer algum sentido e usar atributos personalizados é usar o Reflection . Portanto, se você não usar reflexão em tempo de execução para buscá-los e decorar algo com um atributo personalizado, não espere que aconteça muita coisa.
  • O tempo de criação dos atributos não é determinístico. Eles são instanciados pelo CLR e você não tem absolutamente nenhum controle sobre eles.
Darin Dimitrov
fonte
3
Onde, em qual função / classe, devo `usar reflexão para buscá-la`
Hasan A Yousef
@Hasan A Yousef, por exemplo no Entity Framework existe o atributo "Key" que diz ao framework: Esta propriedade deve ser considerada como Chave Primária. Na criação de ORM, os atributos são muito úteis
Parsa
Como você acessa um atributo personalizado em uma propriedade e não em uma classe?
Canvas
docs.microsoft.com/en-us/dotnet/standard/attributes/… apenas para completar, esta página do msdn resume muito bem
Barış Akkurt
Com os genéricos, é muito, muito mais fácil obter os tipos:var value = typeof(Foo).GetCustomAttributes<MyCustomAttribute>().First().SomeProperty;
jpaugh
27

Utilizando / copiando a ótima resposta de Darin Dimitrov, veja como acessar um atributo personalizado em uma propriedade e não em uma classe:

A propriedade decorada [da classe Foo]:

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

Buscando:

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;
}

Você pode lançar isso em um loop e usar reflexão para acessar este atributo personalizado em cada propriedade da classe Foo, também:

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
    }
}

Muito obrigado a você, Darin !!

Hopper
fonte
como estenderíamos isso se não soubéssemos quais tipos de atributos existem em uma propriedade? object[] attribute = propertyInfo.GetCustomAttributes(typeof(???), true);Eu só quero iterar sobre todos eles e chamar um método m1()de cada atributo desconhecido
heyNow
0

A resposta curta é para criar um atributo em c #, você só precisa herdá-lo da classe Attribute, apenas isto :)

Mas aqui vou explicar os atributos em detalhes:

basicamente atributos são classes que podemos usar para aplicar nossa lógica a assemblies, classes, métodos, propriedades, campos, ...

Em .Net, a Microsoft forneceu alguns atributos predefinidos como atributos obsoletos ou de validação como ([obrigatório], [StringLength (100)], [intervalo (0, 999,99)]), também temos tipos de atributos como ActionFilters em asp.net que pode ser muito útil para aplicar nossa lógica desejada aos nossos códigos (leia este artigo sobre filtros de ação se você for apaixonado por aprender)

um outro ponto, você pode aplicar um tipo de configuração em seu atributo via AttibuteUsage.

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

Quando você decora uma classe de atributo com AttributeUsage, pode dizer ao compilador c # onde vou usar este atributo: vou usar isso em classes, em assemblies em propriedades ou em ... e meu atributo tem permissão para usar várias vezes em alvos definidos (classes, assemblies, propriedades, ...) ou não ?!

Após esta definição sobre os atributos, vou mostrar um exemplo: Imagine que queremos definir uma nova aula na universidade e queremos permitir que apenas administradores e mestres em nossa universidade definam uma nova aula, ok?

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();
        }
    }


}

No mundo real da programação, talvez não usemos essa abordagem para usar atributos e eu disse isso por causa de seu ponto educacional no uso de atributos

Hamid
fonte