O que são atributos no .NET?

206

O que são atributos no .NET, para que servem e como crio meus próprios atributos?

Corey
fonte

Respostas:

146

Metadados. Dados sobre seus objetos / métodos / propriedades.

Por exemplo, posso declarar um Atributo chamado: DisplayOrder para poder controlar facilmente em que ordem as propriedades devem aparecer na interface do usuário. Eu poderia então anexá-lo a uma classe e escrever alguns componentes da GUI que extraem os atributos e ordenam os elementos da interface do usuário adequadamente.

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

Garantindo que SomeInt seja sempre exibido antes de SomeDate ao trabalhar com meus componentes personalizados da GUI.

No entanto, você os verá mais comumente usados ​​fora do ambiente de codificação direta. Por exemplo, o Windows Designer os usa extensivamente para saber como lidar com objetos personalizados. Usando o BrowsableAttribute assim:

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

Diz ao designer para não listar isso nas propriedades disponíveis na janela Propriedades no momento do design, por exemplo.

Você também pode usá-los para geração de código, operações de pré-compilação (como pós-Sharp) ou operações em tempo de execução como Reflection.Emit. Por exemplo, você pode escrever um pouco de código para criação de perfil que envolva de forma transparente todas as chamadas que seu código faz e cronometra. Você pode "optar por não participar" do tempo por meio de um atributo que você coloca em métodos específicos.

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

Declará-los é fácil, basta criar uma classe que herda de Attribute.

public class DisplayOrderAttribute : Attribute
{
    private int order;

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

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

E lembre-se de que quando você usa o atributo, pode omitir o sufixo "attribute", o compilador o adicionará.

NOTA: Os atributos não fazem nada sozinhos - é necessário que haja outro código que os use. Às vezes, esse código foi escrito para você, mas às vezes você precisa escrevê-lo. Por exemplo, o compilador C # se preocupa com algumas e certas estruturas de uso de algumas estruturas (por exemplo, o NUnit procura [TestFixture] em uma classe e [Test] em um método de teste ao carregar um assembly).
Portanto, ao criar seu próprio atributo personalizado, lembre-se de que isso não afetará o comportamento do seu código. Você precisará escrever a outra parte que verifica os atributos (via reflexão) e agir sobre eles.

Quibblesome
fonte
32
Para o que vale a pena, esta é uma lista de todos os atributos do .NET (embutidos): msdn.microsoft.com/en-us/library/aa311259(VS.71).aspx
wprl
1
Como você usaria o "SomeProfilingMethod" como um atributo?
RayLoveless
@RayLoveless não é um atributo, SomeProfilingMethod é o código de instrumentação que está procurando atributos de criação de perfil. Especificamente no exemplo, procurei um atributo "opt-out" (NoTimingAttribute) em oposição a um atributo "opt-in". A ideia é que isso cronometra tudo.
Quibblesome
@ Quibblesome, você poderia adicionar algo como "Os atributos não fazem nada sozinhos - é necessário que haja outro código para usá-los (o compilador se preocupa com o casal, estruturas diferentes usam um pouco). A criação de atributos não afeta o comportamento do código - você precisa escrever a outra parte que verifica os atributos (via reflexão) e age sobre eles ". (ou posso fazer isso se você estiver bem). Muitas pessoas esperam que atributos funcionem magicamente e nenhuma das respostas aqui esclarece isso. (ou apenas um link para stackoverflow.com/questions/4879521/… que o cobre) #
Alexei Levenkov
somente se você parar de usar o Bing. Nah. j / k Eu uso o DuckDuckGo como primário, que usa principalmente o Bing iirc. :)
Quibblesome
36

Muitas pessoas responderam, mas ninguém mencionou isso até agora ...

Os atributos são usados ​​fortemente com reflexão. A reflexão já é bem lenta.

Vale a pena marcar seus atributos personalizados como sealedclasses para melhorar o desempenho em tempo de execução.

Também é uma boa ideia considerar onde seria apropriado usar esse atributo e atribuir seu atributo (!) Para indicar isso via AttributeUsage. A lista de usos de atributos disponíveis pode surpreendê-lo:

  • Montagem
  • Módulo
  • Classe
  • Struct
  • Enum
  • Construtor
  • Método
  • Propriedade
  • Campo
  • Evento
  • Interface
  • Parâmetro
  • Delegar
  • Valor de retorno
  • GenericParameter
  • Tudo

Também é legal que o atributo AttributeUsage faça parte da assinatura do atributo AttributeUsage. Whoa para dependências circulares!

[AttributeUsageAttribute(AttributeTargets.Class, Inherited = true)]
public sealed class AttributeUsageAttribute : Attribute
Drew Noakes
fonte
13

Os atributos são um tipo de metadados para marcar classes. Isso geralmente é usado no WinForms, por exemplo, para ocultar controles da barra de ferramentas, mas pode ser implementado em seu próprio aplicativo para permitir que instâncias de classes diferentes se comportem de maneiras específicas.

Comece criando um atributo:

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

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

Todas as classes de atributos devem ter o sufixo "Atributo" para serem válidas.
Depois disso, crie uma classe que use o atributo

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

Agora você pode verificar uma classe específica ' SortOrderAttribute(se houver) fazendo o seguinte:

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

Se você quiser ler mais sobre isso, sempre pode conferir o MSDN, que tem uma descrição muito boa.
Espero que isso tenha ajudado você!

Patrik Svensson
fonte
5

Um atributo é uma classe que contém algumas funcionalidades que você pode aplicar aos objetos no seu código. Para criar um, crie uma classe que herda de System.Attribute.

Quanto ao que eles servem ... há usos quase ilimitados para eles.

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

O Smurf
fonte
1
"funcionalidade" é a palavra errada aqui; eles são metadados, não funcionalidade
Marc Gravell
5

Os atributos são como metadados aplicados a classes, métodos ou assemblies.

Eles são bons para diversas coisas (visualização do depurador, marcando como obsoleto, marcando como serializável, a lista é interminável).

Criar seus próprios personalizados é fácil como torta. Começa aqui:

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

Stu
fonte
5

No projeto em que estou trabalhando, há um conjunto de objetos de interface do usuário de vários tipos e um editor para montar esses objetos para criar páginas para uso no aplicativo principal, um pouco como o designer de formulários no DevStudio. Esses objetos existem em sua própria montagem e cada objeto é uma classe derivada UserControle possui um atributo personalizado. Este atributo é definido assim:

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

e eu o aplico a uma classe como esta:

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

que é o que os pôsteres anteriores disseram.

Para usar o atributo, o editor possui um Generic::List <Type>contendo os tipos de controle. Há uma caixa de listagem da qual o usuário pode arrastar e soltar na página para criar uma instância do controle. Para preencher a caixa de listagem, obtenho ControlDescriptionAttributeo controle e preencho uma entrada na lista:

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

Nota: o acima é C ++ / CLI, mas não é difícil converter para C # (sim, eu sei, C ++ / CLI é uma abominação, mas é com isso que tenho que trabalhar :-()

Você pode colocar atributos na maioria das coisas e há toda uma gama de atributos predefinidos. O editor mencionado acima também procura atributos customizados nas propriedades que descrevem a propriedade e como editá-la.

Depois de ter toda a idéia, você se perguntará como viveu sem eles.

Skizz
fonte
4

Como dito, os atributos são relativamente fáceis de criar. A outra parte do trabalho está criando o código que o usa. Na maioria dos casos, você usará a reflexão no tempo de execução para alterar o comportamento com base na presença de um atributo ou de suas propriedades. Também existem cenários em que você inspecionará atributos no código compilado para fazer algum tipo de análise estática. Por exemplo, os parâmetros podem ser marcados como não nulos e a ferramenta de análise pode usá-lo como uma dica.

Usar os atributos e conhecer os cenários apropriados para seu uso é a maior parte do trabalho.

Denis Phillips
fonte
3

Os atributos são, essencialmente, bits de dados que você deseja anexar aos seus tipos (classes, métodos, eventos, enumerações, etc.)

A idéia é que, em tempo de execução, algum outro tipo / estrutura / ferramenta consulte seu tipo para obter informações no atributo e atue sobre ele.

Portanto, por exemplo, o Visual Studio pode consultar os atributos em um controle de terceiros para descobrir quais propriedades do controle devem aparecer no painel Propriedades em tempo de design.

Os atributos também podem ser usados ​​na Programação Orientada a Aspectos para injetar / manipular objetos em tempo de execução com base nos atributos que os decoram e adicionam validação, registro em log etc. aos objetos sem afetar a lógica de negócios do objeto.

urini
fonte
2

Para começar a criar um atributo, abra um arquivo de origem C #, digite attributee pressione [TAB]. Ele será expandido para um modelo para um novo atributo.

Jay Bazuzi
fonte
6
Como isso responde à pergunta? deve ser um comentário, não uma resposta.
Gdoron está apoiando Monica
1

Os atributos também são comumente usados ​​para programação orientada a aspectos. Para um exemplo disso, confira o projeto PostSharp .

Josh G
fonte