¿Se pueden agregar atributos dinámicamente en C #?

143

¿Es posible agregar atributos en tiempo de ejecución o cambiar el valor de un atributo en tiempo de ejecución?

Jon Turner
fuente

Respuestas:

67

Los atributos son metadatos estáticos. Los ensamblados, módulos, tipos, miembros, parámetros y valores de retorno no son objetos de primera clase en C # (por ejemplo, la System.Typeclase es simplemente una representación reflejada de un tipo). Puede obtener una instancia de un atributo para un tipo y cambiar las propiedades si pueden escribirse, pero eso no afectará el atributo a medida que se aplica al tipo.

Mark Cidade
fuente
68

Esto realmente depende de lo que estés tratando de lograr exactamente.

El material System.ComponentModel.TypeDescriptor se puede usar para agregar atributos a tipos, propiedades e instancias de objetos, y tiene la limitación de que también debe usarlo para recuperar esas propiedades. Si está escribiendo el código que consume esos atributos, y puede vivir dentro de esas limitaciones, entonces definitivamente lo sugeriría.

Hasta donde yo sé, el control PropertyGrid y la superficie de diseño del estudio visual son las únicas cosas en el BCL que consumen el material TypeDescriptor. De hecho, así es como hacen aproximadamente la mitad de las cosas que realmente necesitan hacer.

Alex Lyman
fuente
77
En realidad, la mayoría de los usos de enlace de datos TypeDescriptor, no solo PropertyGrid.
Marc Gravell
1
Cualquier solución para agregar atributos de metadatos de propiedad en un proyecto de Silverlight (¿dónde TypeDescriptory dónde TypeDescriptionProviderno se implementan?)
Shimmy Weitzhandler
1
Es importante tener en cuenta que TypeDescriptor.GetAttributes () no maneja atributos duplicados. Solo selecciona el último del tipo de atributo. Ex [Attr(1), Attr(2), Attr(3)]solo Attr(3)se encuentra.
ohmusama
11

No puedes Una solución alternativa podría ser generar una clase derivada en tiempo de ejecución y agregar el atributo, aunque esto probablemente sea un poco exagerado.

petr k.
fuente
10

Bueno, para ser diferente, encontré un artículo que hace referencia al uso de Reflection.Emit para hacerlo.

Aquí está el enlace: http://www.codeproject.com/KB/cs/dotnetattributes.aspx , también querrá ver algunos de los comentarios al final del artículo, porque se discuten los posibles enfoques.

torial
fuente
10
Tenga en cuenta que puede crear atributos en tiempo de ejecución con las clases Reflection.Emit, PERO puede vincularlos a las clases que ha creado con el paquete Emit y no a las existentes.
Panos
qué respuesta inútil =)) a todos nos interesan las clases existentes aquí, no las dinámicas.
Desesperado
@Hopeless, puedes subclasificar YourClassen YourRuntimeClassWithAttributes.
Motes
@Motes no está seguro de lo que quiere decir, todas mis clases están definidas de antemano, eso significa que todas las clases base (que heredan mis clases) también deben definirse / determinarse de antemano. No puedo pensar de ninguna manera para que esté involucrado con algo creado dinámicamente usando Reflection.Emit.
Sin esperanza
1
@Hopeless, si quisiera agregar atributos dinámicamente a una clase existente YourClass, podría subclasificarla en tiempo de ejecución y generar una clase idéntica con un nombre ligeramente diferente que también tenga los atributos deseados creados dinámicamente, y el polimorfismo permitirá que el código de verificación de tipos aún identifique tu clase base
Motes
4

No, no es.

Los atributos son metadatos y se almacenan en forma binaria en el ensamblado compilado (es por eso que solo puede usar tipos simples en ellos).

Thomas Danecker
fuente
3

No lo creo Incluso si me equivoco, lo mejor que puede esperar es agregarlos a un Tipo completo, nunca una instancia de un Tipo.

Joel Coehoorn
fuente
22
TypeDescriptor.AddAttributes (Object, Attribute []) agrega atributos de nivel de clase a la instancia del componente de destino.
Peter Wone
3

Si necesita algo para poder agregar dinámicamente, los atributos de C # no son la forma. Considera almacenar los datos en xml. Recientemente hice un proyecto que comencé con w / atributos, pero finalmente me mudé a la serialización w / xml.

Darren Kopp
fuente
1
tal vez no sea una manera hermosa, pero es la forma en que muchas otras bibliotecas eligen usar, y para personalizar el comportamiento de esas bibliotecas, tenemos el requisito de jugar con la reflexión =)) realmente un punto muerto.
Desesperado
3

¿Por qué lo necesitas? Los atributos proporcionan información adicional para la reflexión, pero si sabe externamente qué propiedades desea, no las necesita.

Puede almacenar metadatos externos de manera relativamente fácil en una base de datos o archivo de recursos.

Keith
fuente
1
Eliminación de repeticiones. ¿No sería útil si una clase pudiera generar automáticamente atributos basados ​​en el código dentro de la clase? Estoy tratando de descubrir algo como esto para reducir la repetitiva en los objetos CLR de SQL. Sería fácil en otros idiomas ... ver paulgraham.com/avg.html
Duncan Bayne
1

Lo intenté mucho con System.ComponentModel.TypeDescriptor sin éxito. Eso no significa que no pueda funcionar, pero me gustaría ver el código para eso.

En contraparte, quería cambiar algunos valores de Atributo. Hice 2 funciones que funcionan bien para ese propósito.

        // ************************************************************************
        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
fuente