Atributos en .NET. Atributos del framework (I)

ObsoleteAttribute

El atributo "Obsolete" nos permite marcar un elemento de código como obsoleto y asignar un comentario o mensaje de aviso en caso de que se utilice dicho elemento de código. Adicionalmente permite pasar un valor booleano que indique si el atributo se comportará como un error o como un warning.

El uso del atributo resulta útil conforme nuestro código va evolucionado. Quizá una función o una clase que diseñamos hace tiempo para una biblioteca ya no sea la forma más eficiente de hacer las cosas, de forma que queremos sustituir la función o la clase con otra nueva. En este caso el uso del atributo obsolete nos permite mantener la clase o función de forma que un desarrollador que estuviera utilizando la función no se encuentre con que ha desaparecido sin saber por qué sino con un warning (o un error en caso de que así lo hallamos definido) avisándole de que cambie y, preferiblemente, indicándole los pasos a realizar. Vamos a ver un ejemplo muy típico:

[Obsolete("Esta clase esta obsoleta, utilice NuevaFoo en su lugar")]
void Foo(int x)

ConditionalAttribute

El atributo "Conditional" se puede aplicar sobre una clase o método. Este atributo se comporta de forma muy similar a la directiva de precompilación #ifdef (que permite hacer que cierto código solo sea compilado en función de si una determinada directiva ha sido previamente definida con #define). Veamos un ejemplo típico de log

#ifdef Loggin
public void LogMsg(string msg)
{
  // Código de log
  Console.Writeln(msg);
}
#endif

public void FuncionPrueba
{
  // Algún codigo de la función
  #ifdef Loggin
  LogMsg("Prueba");
  #endif
}

De esta forma cada vez la definición de la directiva "Loggin" permite activar o desactivar el registro de la aplicación pero a cambio "ensuciamos" bastante el código de forma que queda mucho menos claro. En cambio, utilizando el atributo el código quedaría:

[Conditional("Loggin")]
public void LogMsg(string msg)
{
  // Código de log
  Console.Writeln(msg);
}

public void FuncionPrueba
{
  // Algún codigo de la función
  LogMsg("Prueba");
}

De forma que la definición del atributo se realiza una sola vez por lo que evitamos todos los #ifdef y, lo que es un beneficio adicional, el compilador elimina automáticamente todas las llamadas a un método o a una clase de forma que no tenemos que preocuparnos por poner los #ifdef en cada llamada.

Información de ensamblado

.NET proporciona una serie de atributos de ensamblado que nos van a permitir definir aspectos tales como el titulo, la descripción o la versión de un determinado ensamblado así como otras características de un ensamblado.

Existen dos tipos fundamentales de atributos de ensamblado, los atributos de información general, como los mencionados anteriormente, cuyo objetivo es la de proporcionar información adicional sobre el ensamblado, y los atributos de comportamiento que permiten afectar el funcionamiento del ensamblado en runtime así como el comportamiento del compilador al tratarlo.

Información general

Los atributos de esta categoría comprenden

AssemblyCompanyAttribute Identifica la compañía o empresa propietaria del ensamblado
AssemblyCopyrightAttribute Proporciona información sobre el copyright del ensamblado
AssemblyFileVersionAttribute Indica la versión del archivo Win32 del ensamblado
AssemblyInformationalVersionAttribute Indica la versión de información del ensamblado que no es usada por el runtime
AssemblyProductAttribute Indica la información sobre el producto
AssemblyTrademarkAttribute Indica la información de trademark (marca registrada)
AssemblyDescriptionAttribute Permite proporcionar la descripción del ensamblado
AssemblyTitleAttribute Permite indicar el título del ensamblado

Por ejemplo:

[assembly: AssemblyTitle("Titulo del atributo")]
[assembly: AssemblyDescription("Descripción del atributo")]
[assembly: AssemblyCompany("Empresa")]
[assembly: AssemblyProduct("Producto")]
[assembly: AssemblyCopyright("2007")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("2.0.0.0")]

CLS Compliant

Para explicar este atributo hay que hacer una pequeña introducción de dos conceptos de .NET el (Common Type System) y el CLS (Common Language Specification). La cuestión, resumida, consiste en que .NET proporciona intercompatibilidad entre ensamblados generados en los varios lenguajes definidos (de hecho .NET está abierto al desarrollo de compiladores para cualquier lenguaje) siempre que se ajusten a la especificación del CLS.

Ahora bien, la especificación del CTS define un rango más amplio de tipos que el que define el CLS. Esto quiere decir que, pese a que un compilador puede ajustarse al CTS para generar ensamblados correctos a .NET no se garantiza que dichos ensamblados puedan ser leídos o utilizados por otros ensamblados si no se ajustan a la especificación CLS (es posible que puedan ser utilizados, lo único es que no hay garantías).

Por ejemplo el compilador de C# permite el uso de tipos sin signo como por ejemplo el tipo uint de C# que sin embargo no forma parte de la especificación CLS, de forma que si tenemos una clase que "expone" (y eso es importante puesto que si no es visible entonces no hay problemas) una propiedad de valor uint, entonces esa clase no sigue el estandar CLS por lo que no se puede garantizar que, por ejemplo, funcione con el compilador de J# si el ensamblado se utiliza desde ahí (aunque sin embargo es, por ejemplo, muy probable que funcione desde Visual Basic .NET).

Una vez dicho todo lo anterior el uso del atributo CLS Compliant es razonablemente sencillo, bastará con declararlo antes del elemento especificado (puede declararse sobre cualquier elemento) indicando si el elemento o clase es compatible con CLS o no.

[CLSComplian(true)]
public class MyClass
{
  // Esto no da problema
  public int MyProp1{}
  // Esto dara un error de compilación
  public uint MyProp2{}
}

La cosa es que, al señalar la clase como "CLSCompliant" no solo indicamos que lo es sino que además el compilador se encargará de comprobar que todo es correcto, es decir, que el código de hecho es "CLS Compliant" indicándolo en caso de que haya algún error como en el ejemplo anterior.

Atributos de componente

La creación de componentes de usuario en .NET es algo extraordinariamente sencillo, desde Visual Studio basta con pinchar en añadir control de usuario desde el menú contextual del proyecto para añadir un nuevo control de usuario y en código basta básicamente con heredar de la clase UserControl.

El editor de Visual Studio es lo suficientemente inteligente como para añadir nuestras propiedades de forma automática al editor de propiedades cuando se pone el foco en nuestro control, de forma que si tenemos el siguiente control simple:

public class MiControl : UserControl
{              
  private System.ComponentModel.Container components = null;
       
  private string texto;
       
  public string Texto{
    get { return texto;}
        set { texto = value;}
  }

  public UserControl1()
  {
    // This call is required by the Windows.Forms Form Designer.
        InitializeComponent();
        // TODO: Add any initialization after the InitializeComponent call
        }

La propiedad Texto aparecerá directamente y podrá ser modificada en el editor de propiedades. Sin embargo el editor de propiedades es bastante complejo y .NET proporciona toda una serie de atributos que nos van a permitir proporcionar información adicional sobre nuestra clase y nuestros componentes.

DefaultProperty y DefaultEvent

DefaultProperty nos permite especificar la propiedad que actuará como propiedad por defecto para el componente. La propiedad por defecto de un componente es aquella que va a recibir el foco cuando seleccionamos el componente y la propiedad de la que veníamos no existe en el nuevo componente.

Por ejemplo, supongamos que pinchamos en un DataGrid y hacemos click en la propiedad Caption Text, cuando volvemos a nuestro componente que no posee esa propiedad el foco va a la propiedad que esté definida como propiedad por defecto. Este atributos solo puede aplicarse sobre la clase que define el componente y admite como parametro el nombre de la propiedad.

DefaultEvent realiza la misma función que DefaultProperty pero para el evento por defecto, este es el evento que se autogenerará cuando hagamos dobleclick sobre el control, por ejemplo:

[DefaultProperty("Texto")]
[DefaultEvent("Click")]
public class MiControl : UserControl

Browsable, Description, Category

Por defecto todas las propiedades publicas definidas en el componente se muestran en el editor de propiedades. El atributo Browsable, que acepta un booleano como argumento, nos permite cambiar este comportamiento ocultando aquellas propiedades que no deseemos que aparezcan.

El atributo Description nos permite especificar la descripción asociada a la propiedad de forma que se muestre en el editor de propiedades y por último el atributo Category nos permite especificar la categoría del editor de propiedades en la que se mostrará la propiedad dada.

[Browsable(true)]
[Description("Establece el texto del control")]
[Category("Comunes")]
public string Texto{ get; set; }

Bindable

Este atributo permite especificar que una determinada propiedad debe aparecer en la sección de Data Bindings y, por lo tanto, habilitar la posibilidad de asociarla mediante un binding a una fuente de datos.

[Bindable(true)]
public string Texto{ get; set;}

De esta forma el editor de propiedades mostrará lo siguiente:

prop_editor.png

0
No votes yet
Your rating: None