Os atributos podem ser adicionados dinamicamente em C #?

143

É possível adicionar atributos no tempo de execução ou alterar o valor de um atributo no tempo de execução?

Jon Turner
fonte

Respostas:

67

Atributos são metadados estáticos. Assemblies, módulos, tipos, membros, parâmetros e valores de retorno não são objetos de primeira classe em C # (por exemplo, a System.Typeclasse é apenas uma representação refletida de um tipo). Você pode obter uma instância de um atributo para um tipo e alterar as propriedades se elas forem graváveis, mas isso não afetará o atributo conforme é aplicado ao tipo.

Mark Cidade
fonte
68

Isso realmente depende do que exatamente você está tentando realizar.

O material System.ComponentModel.TypeDescriptor pode ser usado para adicionar atributos a tipos, propriedades e instâncias de objetos e tem a limitação de que você deve usá-lo para recuperar essas propriedades também. Se você está escrevendo o código que consome esses atributos e pode viver dentro dessas limitações, eu definitivamente o sugeriria.

Até onde eu sei, o controle PropertyGrid e a superfície de design do visual studio são as únicas coisas na BCL que consomem o material TypeDescriptor. De fato, é assim que eles fazem cerca da metade das coisas que realmente precisam fazer.

Alex Lyman
fonte
7
Na verdade, a maioria dos usos de ligação de dados TypeDescriptor- não apenas PropertyGrid.
Marc Gravell
1
Qualquer solução para adicionar atributos de propriedade-metadados em um projeto do Silverlight (onde TypeDescriptore TypeDescriptionProvidernão são implementados?
Shimmy Weitzhandler
1
Importante observar, TypeDescriptor.GetAttributes () não manipula atributos duplicados. Ele seleciona apenas o último do tipo de atributo. [Attr(1), Attr(2), Attr(3)]Apenas ex Attr(3)é encontrado.
Ohmusama
11

Você não pode. Uma solução alternativa pode ser gerar uma classe derivada em tempo de execução e adicionar o atributo, embora isso provavelmente seja um exagero.

petr k.
fonte
10

Bem, só para ser diferente, encontrei um artigo que faz referência ao Reflection.Emit para fazer isso.

Aqui está o link: http://www.codeproject.com/KB/cs/dotnetattributes.aspx , você também deseja examinar alguns dos comentários na parte inferior do artigo, porque abordagens possíveis são discutidas.

histórico
fonte
10
Observe que você pode criar atributos em tempo de execução com as classes Reflection.Emit, MAS você pode vinculá-los às classes que você criou com o pacote Emit e não às existentes.
Panos
que resposta inútil =)) todos nos preocupamos com as classes existentes aqui, e não com as dinâmicas.
29319 Hopeless
@ Hopeless, você pode subclasse YourClassem YourRuntimeClassWithAttributes.
Motes
@Motes não sabe o que quer dizer, minhas classes são todas definidas de antemão, ou seja, todas as classes base (que minhas classes herdam) também devem ser definidas / determinadas com antecedência. Não consigo pensar em nenhuma maneira de se envolver com algo criado dinamicamente usando Reflection.Emit.
Hopeless
1
@Hopeless, se você quiser adicionar atributos dinamicamente a uma classe existente YourClass, poderá subclassificá-lo em tempo de execução e gerar uma classe idêntica com um nome ligeiramente diferente que também tenha os atributos criados dinamicamente desejados, e o polimorfismo permitirá que o código de verificação de tipo ainda identifique sua classe básica.
Motes
4

Não, não é.

Os atributos são metadados e armazenados em formato binário no assembly compilado (é também por isso que você só pode usar tipos simples).

Thomas Danecker
fonte
3

Eu não acredito nisso. Mesmo se eu estiver errado, o melhor que você pode esperar é adicioná-los a um Type inteiro, nunca a uma instância de um Type.

Joel Coehoorn
fonte
22
TypeDescriptor.AddAttributes (Object, Attribute []) adiciona atributos em nível de classe à instância do componente de destino.
Peter Wone
3

Se você precisar de algo para poder adicionar dinamicamente, os atributos c # não são o caminho. Procure armazenar os dados em xml. Recentemente, eu fiz um projeto que iniciei com atributos, mas que acabei mudando para serialização com xml.

Darren Kopp
fonte
1
talvez não seja uma maneira bonita, mas é a maneira que muitas outras bibliotecas optam por usar, e para personalizar o comportamento dessas bibliotecas, temos o requisito de brincar com a reflexão =)) realmente um impasse.
29319 Hopeless
3

Por que você precisa? Os atributos fornecem informações extras para reflexão, mas se você souber externamente quais propriedades deseja, não precisará delas.

Você pode armazenar metadados externamente com relativa facilidade em um banco de dados ou arquivo de recurso.

Keith
fonte
1
Eliminação de clichês. Não seria útil se você pudesse ter uma classe automaticamente gerando atributos com base no código da classe? Estou tentando descobrir algo assim para reduzir o clichê nos objetos SQL CLR. Seria fácil em outros idiomas ... consulte paulgraham.com/avg.html
Duncan Bayne
1

Eu tentei muito com System.ComponentModel.TypeDescriptor sem sucesso. Isso não significa que não pode funcionar, mas eu gostaria de ver o código para isso.

Em contrapartida, eu queria alterar alguns valores de atributo. Eu fiz duas funções que funcionam bem para esse fim.

        // ************************************************************************
        public static void SetObjectPropertyDescription(this Type typeOfObject, string propertyName,  string description)
        {
            PropertyDescriptor pd = TypeDescriptor.GetProperties(typeOfObject)[propertyName];
            var att = pd.Attributes[typeof(DescriptionAttribute)] as DescriptionAttribute;
            if (att != null)
            {
                var fieldDescription = att.GetType().GetField("description", BindingFlags.NonPublic | BindingFlags.Instance);
                if (fieldDescription != null)
                {
                    fieldDescription.SetValue(att, description);
                }
            }
        }

        // ************************************************************************
        public static void SetPropertyAttributReadOnly(this Type typeOfObject, string propertyName, bool isReadOnly)
        {
            PropertyDescriptor pd = TypeDescriptor.GetProperties(typeOfObject)[propertyName];
            var att = pd.Attributes[typeof(ReadOnlyAttribute)] as ReadOnlyAttribute;
            if (att != null)
            {
                var fieldDescription = att.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
                if (fieldDescription != null)
                {
                    fieldDescription.SetValue(att, isReadOnly);
                }
            }
        }
Eric Ouellet
fonte