2010-04-20 11 views
47

Mis búsquedas siguen apareciendo solo guías que explican cómo usar y aplicar atributos a una clase. Quiero aprender a crear mis propias clases de atributos y la mecánica de cómo funcionan.¿Cómo funcionan las clases de atributos?

¿Cómo se crean las clases de atributos? ¿Se crean instancias cuando se instancia la clase a la que se aplican? ¿Hay una instancia para cada clase instanciada a la que se aplica? P.ej. si aplico la clase SerializableAttribute a una clase MyData, e instauro 5 instancias MyData, ¿habrá 5 instancias de la clase SerializbleAttribute creadas detrás de las escenas? ¿O solo hay una instancia compartida entre todos ellos?

¿Cómo acceden las instancias de clase de atributo a la clase a la que están asociadas? ¿Cómo accede una clase SerializableAttribute a la clase a la que se aplica para que pueda serializar sus datos? ¿Tiene algún tipo de propiedad SerializableAttribute.ThisIsTheInstanceIAmAppliedTo? :) ¿O funciona en sentido inverso que cada vez que serializo algo, la función Serialize que paso la instancia de MyClass pasará reflexivamente por los Atributos y encontrará la instancia SerialiableAttribute?

+4

+1 Todos para respuestas útiles. Ahora entiendo claramente que los atributos no tienen ningún comportamiento y no cambian el comportamiento de la clase a la que se aplican. Simplemente son metadatos extra anclados en una clase. Entonces, lo que percibí como "comportamiento" no era realmente el código de la clase de atributo, sino solo otro código, como el método Serialize, que interpreta los metadatos y los usa para realizar acciones condicionalmente (como un método Serialize que mira el atributo Serializable). – AaronLS

+0

Vea un buen ejemplo de cuándo se ejecutan los constructores de atributos: http://stackoverflow.com/a/1168590/84206 – AaronLS

Respuesta

32

No he usado atributos en mi trabajo diario, pero he leído sobre ellos. También hice algunas pruebas para respaldar lo que voy a decir aquí. Si estoy equivocado en cualquier lugar, no dude en decirme esto :)

Por lo que sé, los atributos no están actuando como clases regulares. No se crean instancias cuando se crea un objeto al que se aplican, ni una instancia estática, ni 1 por cada instancia del objeto. Ni acceden a la clase a la que se aplican ...

En su lugar, actúan como propiedades (¿atributos ?: P) de la clase. No como la .NET clase propiedades, más como en el tipo de propiedad "una propiedad del vidrio es transparencia".Puede verificar qué atributos se aplican a una clase desde la reflexión, y luego actuar en consecuencia. En esencia, son metadatos que se adjuntan a la definición de clase, no los objetos de ese tipo.

Puede tratar de obtener la lista de atributos en una clase, método, propiedad, etc., etc. Cuando obtenga la lista de estos atributos, aquí es donde se crearán las instancias. Entonces puede actuar sobre los datos dentro de estos atributos.

E.g. las tablas de Linq, las propiedades tienen atributos que definen a qué tabla/columna se refieren. Pero estas clases no usan estos atributos. En cambio, DataContext verificará los atributos de estos objetos cuando convertirá los árboles de expresión de linq en código SQL.

Ahora, para ver algunos ejemplos reales. He ejecutado estos en LinqPad, así que no se preocupe por el método extraño Dump(). He sustituido con Console.WriteLine para hacer que el código sea más fácil de entender para la gente que no sabe acerca de él :)

void Main() 
{ 
    Console.WriteLine("before class constructor"); 
    var test = new TestClass(); 
    Console.WriteLine("after class constructor"); 

    var attrs = Attribute.GetCustomAttributes(test.GetType()).Dump(); 
    foreach(var attr in attrs) 
     if (attr is TestClassAttribute) 
      Console.WriteLine(attr.ToString()); 
} 

public class TestClassAttribute : Attribute 
{ 
    public TestClassAttribute() 
    { 
     DefaultDescription = "hello"; 
     Console.WriteLine("I am here. I'm the attribute constructor!"); 
    } 
    public String CustomDescription {get;set;} 
    public String DefaultDescription{get;set;} 

    public override String ToString() 
    { 
     return String.Format("Custom: {0}; Default: {1}", CustomDescription, DefaultDescription); 
    } 
} 

[Serializable] 
[TestClass(CustomDescription="custm")] 
public class TestClass 
{ 
    public int Foo {get;set;} 
} 

El resultado de la consola de este método es:

before class constructor 
after class constructor 
I am here. I'm the attribute constructor! 
Custom: custm; Default: hello 

y los Attribute.GetCustomAttributes(test.GetType()) devuelve esta matriz: (la tabla muestra todas las columnas disponibles para todas las entradas .. Así que no, el atributo Serializable no tiene estas propiedades :)) LinqPad Attributes Array

consiguió cualquier mor e preguntas? ¡Siéntete libre de preguntar!

UPD: Lo he visto hacer una pregunta: ¿por qué utilizarlos? Como ejemplo, le contaré sobre la biblioteca XML-RPC.NET. Crea su clase de servicio XML-RPC, con métodos que representarán los métodos xml-rpc. Lo principal en este momento es: en XmlRpc los nombres de los métodos pueden tener algunos caracteres especiales, como puntos. Entonces, puede tener un método flexlabs.ProcessTask() xml rpc.

Se podría definir esta clase de la siguiente manera:

[XmlRpcMethod("flexlabs.ProcessTask")] 
public int ProcessTask_MyCustomName_BecauseILikeIt(); 

Esto me permite nombrar el método en la manera que me gusta, sin dejar de utilizar el nombre público, ya que tiene que ser.

+0

muy buena respuesta. – Dhananjay

6

Sí, se crean instancias con los parámetros que le das.

El atributo no "accede" a la clase. El atributo se adjunta a la lista de atributos de la clase '/ propiedad en los datos de reflexión.

[Serializable] 
public class MyFancyClass 
{ ... } 

// Somewhere Else: 

public void function() 
{ 
    Type t = typeof(MyFancyClass); 
    var attributes = t.GetCustomAttributes(true); 

    if (attributes.Count(p => p is SerializableAttribute) > 0) 
    { 
     // This class is serializable, let's do something with it! 

    }  
} 
+1

Lo pido para que no sea difícil, pero para mejorar mi comprensión: ¿Cuál sería el beneficio de un atributo si solo actúa como una bandera? Otros diseños podrían hacer esto, como si MyFancyClass implementara una interfaz IsSerialiable o una interfaz con una propiedad IsSerializable. ¿Qué es un atributo necesario? ¿Qué función cumple la clase SerializableAttribute, el código en esa misma clase de atributo y los datos/propiedades en esa clase? – AaronLS

+1

@AaronLS puede agregar atributos en tiempo de ejecución. También puede tener la capacidad de conservarlos al leer/escribir objetos en archivos, incluso si el código no sabe qué hacer con ellos. – phkahler

1

No hay mucho tiempo para darle una respuesta más completa, pero puede encontrar los Atributos que se han aplicado a un valor utilizando Reflection. En cuanto a crearlos, heredas de la Clase de Atributo y trabajas desde allí, y los valores que proporcionas con un atributo se pasan al constructor de la clase de Atributo.

Ha sido un tiempo, ya que podría ser capaz de decir ...

Martin

16

atributos son esencialmente datos meta que se puede conectar a varias piezas de su código. Estos metadatos pueden ser intercalados y afectar el comportamiento de ciertas operaciones.

Los atributos se pueden aplicar a casi todos los aspectos de su código. Por ejemplo, los atributos pueden asociarse en el nivel de ensamblaje, como los atributos AssemblyVersion y AssemblyFileVersion, que rigen los números de versión asociados con el ensamblaje.

[assembly: AssemblyVersion("1.0.0.0")] 
[assembly: AssemblyFileVersion("1.0.0.0")] 

Entonces el atributo Serializable por ejemplo se puede aplicar a una declaración de tipo de indicador del tipo como el apoyo a la serialización. De hecho, este atributo tiene un significado especial dentro del CLR y en realidad está almacenado como una directiva especial directamente en el tipo en el IL, esto está optimizado para almacenarse como un indicador de bit que se puede procesar de manera mucho más eficiente, hay algunos atributos en esta naturaleza, que se conocen como atributos pseudo personalizados.

Todavía otros atributos se pueden aplicar a métodos, propiedades, campos, enumeraciones, los valores de retorno, etc Usted puede tener una idea de los posibles objetivos de un atributo se puede aplicar a examinado este enlace http://msdn.microsoft.com/en-us/library/system.attributetargets(VS.90).aspx

Más a esto, puede definir sus propios atributos personalizados que luego se pueden aplicar a los objetivos aplicables a los que se destinan sus atributos. Luego, en tiempo de ejecución su código podría reflejar los valores contenidos en los atributos personalizados y tomar las medidas adecuadas.

Para un ejemplo bastante ingenuo, y esto solo por el simple :) Puede escribir un motor de persistencia que mapeará Clases automáticamente a tablas en su base de datos y correlacionará las propiedades de las columnas Clase a tabla . Se podría empezar por definir dos atributos personalizados

TableMappingAttribute 
ColumnMappingAttribute 

que luego se puede aplicar a sus clases, como ejemplo tenemos una clase Persona

[TableMapping("People")] 
public class Person 
{ 
    [ColumnMapping("fname")] 
    public string FirstName {get; set;} 

    [ColumnMapping("lname")] 
    public string LastName {get; set;} 
} 

Cuando esto compila, aparte del hecho de que el compilador emite los metadatos adicionales definidos por los atributos personalizados, poco más se ve afectado. Sin embargo, ahora puede escribir un PersistanceManager que puede inspeccionar dinámicamente los atributos de una instancia de la clase Person e insertar los datos en la tabla People, asignando los datos en la propiedad FirstName a la columna fname y la propiedad LastName a la columna lname.

En cuanto a su pregunta con respecto a las instancias de los atributos, la instancia del atributo no se crea para cada instancia de su clase. Todas las instancias de personas compartirán la misma instancia de TableMappingAttribute y ColumnMappingAttributes. De hecho, las instancias de atributo solo se crean cuando realmente consultas los atributos la primera vez.

+0

@Chris +1 Solía ​​pensar que los atributos utilizados por la serialización, y los ORM como en su ejemplo, estaban haciendo algo muy mágico en el momento de la compilación para analizar el objeto para que más tarde en el tiempo de ejecución supiera cómo leer/escribir el objetos a/desde archivos binarios/xml (serialización) o bases de datos (ORM). Pensé que tal vez los atributos tenían algún tipo de "ejecutar en código de tiempo de compilación". Así que durante mucho tiempo me pregunté qué cosas buenas podría hacer con los atributos. Después de estudiarlo, me di cuenta de que mi percepción estaba muy lejos de la verdad, pero nunca había descubierto cómo los atributos proporcionaban "comportamientos" personalizados. – AaronLS

6

Piense en los atributos son post-its que están adjuntos a las clases o definiciones de método (incrustadas en los metadatos del ensamblado).

A continuación, puede tener un módulo procesador/corredor/inspector que acepte estos tipos reflejando, busca estos post-its y los maneja de forma diferente. Esto se llama programación declarativa. Usted declara algún comportamiento en lugar de escribir código para ellos en el tipo.

  • El atributo serializable en un tipo declara que está diseñado para ser serializado. El XmlSerializer puede aceptar un objeto de esta clase y hacer lo necesario. Marca los métodos que deben ser serializados/ocultos con los post-its correctos.
  • otro ejemplo sería la NUnit. El corredor NUnit observa los atributos [TestFixture] de todas las clases definidas en el conjunto de destino para identificar las clases de prueba. Luego busca los métodos marcados con el atributo [Test] para identificar las pruebas, que luego ejecuta y muestra los resultados.

Es posible que desee ejecutar a través de this tutorial at MSDN que ha respondido la mayoría de sus preguntas junto con un ejemplo al final. Aunque podrían haber extraído un método llamado Audit(Type anyType); en lugar de duplicar ese código. El ejemplo 'imprime información' mediante la inspección de atributos ... pero puede hacer cualquier cosa en el mismo sentido.

2

Si echa un vistazo a este código fuente abierto descargable LINQ to Active Directory (CodePlex), puede encontrar interesante el mecanismo del archivo Attributes.cs donde Bart De Smet ha escrito todas sus definiciones de clases de atributos. He aprendido atributos allí.

En resumen, puede especializar la clase Attribute y codificar algunas propiedades especializadas para sus necesidades.

public class MyOwnAttributeClass : Attribute { 
    public MyOwnAttributeClass() { 
    } 
    public MyOwnAttributeClass(string myName) { 
     MyName = myName; 
    } 
    public string MyName { get; set; } 
} 

y luego, puede usarlo donde sea que MyOwnAttributeClass sea útil. Puede ser una definición de clase o una definición de propiedad.

[MyOwnAttributeClass("MyCustomerName")] 
public class Customer { 
    [MyOwnAttributeClass("MyCustomerNameProperty")] 
    public string CustomerName { get; set; } 
} 

A continuación, puede conseguirlo a través de la reflexión de este modo:

Attribute[] attributes = typeof(Customer).GetCustomAttribute(typeof(MyOwnAttributeClass)); 

considerar que el atributo se pone entre corchetes es siempre el constructor de su atributo. Entonces, si quieres tener un atributo parametrizado, necesitas codificar tu constructor como tal.

Este código se proporciona tal cual y no se puede compilar. Su propósito es darte una idea de cómo funciona.

De hecho, generalmente desea tener una clase de atributo diferente para una clase que para una propiedad.

Espero que esto ayude!

Cuestiones relacionadas