<?xml version="1.0"  encoding="ISO-8859-1" ?> <!-- encoding="windows-1252" ?> -->
<?xml-stylesheet type="text/xsl" href="../csharp.xsl"?>

<document>
<title>Aspectos avanzados de C#</title>
<author>Francisco Cortijo Bon</author>

<navigation>
 <prev>
  <ref>classes.xml</ref>
  <title>Clases</title>
 </prev>
 <next>
   <ref>program.xml</ref>
   <title>Programas en C#</title>
 </next>
</navigation>



<document>
<tag>exceptions</tag>
<title>Excepciones</title>

<text>
Las excepciones ofrecen varias ventajas respecto a otros métodos 
de notificación de error, como los códigos devueltos 
(órdenes <type>return</type>) ya que ningún error pasa desapercibido 
(las excepciones no pueden ser ignoradas) y no tienen por qué tratarse 
en el punto en que se producen. Los valores no válidos no se siguen 
propagando por el sistema. No es necesario comprobar los códigos devueltos. 
Es muy sencillo agregar código de control de excepciones para aumentar 
la confiabilidad del programa. 
</text>


<text>
Una excepción es cualquier situación de error o comportamiento inesperado 
que encuentra un programa en ejecución. Las excepciones se pueden 
producir a causa de un error en el código o en código al que se 
llama (como una biblioteca compartida), que no estén disponibles 
recursos del sistema operativo, condiciones inesperadas que encuentra 
Common Language Runtime (por ejemplo, código que no se puede comprobar), etc. 
La aplicación se puede recuperar de algunas de estas condiciones, 
pero de otras no. 
</text>

<text>
Las excepciones pueden generarse en un proceso o hebra de 
nuestra aplicación (con la sentencia <type>throw</type>) o 
pueden provenir del entorno de ejecución de la plataforma .NET.
</text>

<text>
En .NET Framework, una excepción es un objeto derivado de la clase 
<type>Exception</type>. 
El mecanismo de control de excepciones en C# es muy parecido al 
de C++ y Java: la excepción se inicia en un área del código 
en que se produce un problema. La excepción asciende por la pila 
hasta que la aplicación la controla o el programa se detiene. 
</text>


<text>
El proceso es el siguiente:
<list>
 <item>La sentencia <type>throw</type> lanza una 
 excepción (una instancia de una clase derivada de 
 <type>System.Exception</type>, que contiene información 
 sobre la excepción: <type>Message</type>, <type>StackTrace</type>, 
 <type>HelpLink</type>, <type>InnerException</type>...).</item>
 <item>El bloque <type>try</type> delimita código que podría 
 generar una excepción.</item>
 <item>El bloque <type>catch</type> indica cómo se manejan 
 las excepciones. Se puede relanzar la excepción capturada o 
 crear una nueva si fuese necesario. 
 Se pueden especificar distintos bloques <type>catch</type> 
 para capturar distintos tipos de excepciones. 
 En ese caso, es recomendable poner primero los más 
 específicos (para asegurarnos de que capturamos la 
 excepción concreta).</item> 
 <item>El bloque <type>finally</type> incluye código que 
 siempre se ejecutará (se produzca o no una excepción).
 </item>
</list>
</text>


<text><b>Ejemplo 1</b></text>

<text>
El siguiente ejemplo pone de manifiesto el flujo de 
ejecución que sigue un programa que lanza y procesa una excepción.
</text>

<example>
<code>
try {  
  Console.WriteLine("try");
  throw new Exception("Mi excepcion");
} 
catch {
  Console.WriteLine("catch");
} 
finally {
  Console.WriteLine("finally");
}
</code>
</example>

<text>La ejecución de este programa produce el siguiente resultado:
<code>
try
catch
finally
</code>
</text>

<!-- 
<image>
<url>image/out-Excep1.gif</url>
</image>
-->


<text><b>Ejemplo 2</b></text>

<text>
Se puede profundizar en el tratamiento de la excepción, por ejemplo, 
comprobando alguna propiedad del objeto <type>Exception</type> generado.
</text>

<text>
La clase <type>Exception</type> es la clase base de la que derivan las 
excepciones. 
La mayoría de los objetos de excepción son instancias de alguna clase 
derivada de <type>Exception</type>, pero se puede iniciar cualquier 
objeto derivado de la clase <type>Object</type> como excepción. 
En casi todos los casos, es recomendable iniciar y detectar sólo 
objetos <type>Exception</type>.
</text>

<text>
La clase <type>Exception</type> tiene varias propiedades que facilitan 
la comprensión de una excepción. Entre éstas destacamos la 
propiedad <type>Message</type>. Esta propiedad proporciona información 
sobre la causa de una excepción. Veamos cómo se utiliza: 
</text>

<example>
<code>
try {  
  Console.WriteLine("try");
  throw new Exception("Mi excepcion");
} 
catch (Exception e) 
{
  Console.WriteLine("catch");
  Console.WriteLine("Excepción detectada: " + e.Message);
}
catch {
  Console.WriteLine("catch");
} 
finally {
  Console.WriteLine("finally");
}
</code>
</example>

<text>
La ejecución de este programa produce el siguiente resultado:
</text>
<code>
try
catch
Excepción detectada: Mi excepcion
finally
</code>


<text>
La mayoría de las clases derivadas de la clase <type>Exception</type> 
no implementan miembros adicionales ni proporcionan más funcionalidad, 
simplemente heredan de <type>Exception</type>. 
Por ello, la información más importante sobre una excepción 
se encuentra en la jerarquía de excepciones, el nombre de la 
excepción y la información que contiene la excepción.
</text>


<text><b>Ejemplo 3</b></text>

<text>
El siguiente ejemplo muestra cómo el uso de execpciones puede controlar 
un número importante de situaciones de error.
</text>

<example>
<code>
  static void Main(string[] args)
  {
    int numerador = 10;
    Console.WriteLine ("Numerador es = {0}", numerador); 
    Console.Write ("Denominador = "); 
    string strDen = Console.ReadLine();

    int denominador, cociente; 

    try 
    {
      Console.WriteLine("--> try");
      denominador = Convert.ToInt16(strDen); 
      cociente = numerador / denominador; 
      Console.WriteLine ("Cociente = {0}", cociente);
    } 

    catch (ArithmeticException e) 
    {
      Console.WriteLine("--> catch");
      Console.WriteLine("Excep. aritmética");
      Console.WriteLine("ArithmeticException Handler: {0}", 
         e.ToString());
    } 
    catch (ArgumentNullException e) 
    {
      Console.WriteLine("--> catch");
      Console.WriteLine("Excep. de argumento nulo");
      Console.WriteLine("ArgumentNullException Handler: {0}", 
         e.ToString());
    } 
    catch (Exception e) 
    {
      Console.WriteLine("--> catch");
      Console.WriteLine("generic Handler: {0}", 
         e.ToString());
    } 
    finally 
    {
      Console.WriteLine("--> finally");
    }
    
    Console.ReadLine();
  }
}
</code>
</example>

<text>Cuando todo funciona sin problemas:</text>

<image>
<url>image/out_Excep2.gif</url>
</image>

<text>Cuando se intenta dividir por cero:</text>

<image>
<url>image/out_Excep3_smll.gif</url>
</image>


<text>Cuando se produce desbordamiento:</text>

<image>
<url>image/out_Excep4_smll.gif</url>
</image>

<text>Cuando se produce otro problema 
(cadena vacía, por ejemplo):</text>

<image>
<url>image/out_Excep5_smll.gif</url>
</image>

<text><b>Ejemplo 4</b></text>

<text>
Hemos visto que pueden conocerse los detalles de la 
excepción que se haya producido. Podemos conocer más detalles usando la 
propiedad <type>StackTrace</type>. Esta propiedad contiene un seguimiento 
de pila que se puede utilizar para determinar dónde se ha producido un error. 
El seguimiento de pila contiene el nombre del archivo de código fuente 
y el número de la línea del programa si está disponible la información 
de depuración. 
</text>

<example>
<code>
<![CDATA[
using System;
using System.Diagnostics;

class ExcepApp
{
  static void Main(string[] args)
  {
    int numerador = 10;
    Console.WriteLine ("Numerador es = {0}", numerador); 

    Console.Write ("Denominador = "); 
    string strDen = Console.ReadLine();

    int denominador, cociente; 

    try 
    {
      Console.WriteLine("--> try");
      denominador = Convert.ToInt16(strDen); 
      cociente = numerador / denominador; 
      Console.WriteLine ("Cociente = {0}", cociente);
    } 

    catch (Exception e) 
    {
      Console.WriteLine("--> catch");
      Console.WriteLine("Generic Handler: {0}", e.ToString());
      Console.WriteLine();

      StackTrace st = new StackTrace(e, true); 

      Console.WriteLine("Traza de la pila:"); 
      for (int i = 0; i < st.FrameCount; i++) {
        StackFrame sf = st.GetFrame(i);
        Console.WriteLine("  Pila de llamadas, Método: {0}",
		        sf.GetMethod() );
        Console.WriteLine("  Pila de llamadas, Línea : {0}",
                sf.GetFileLineNumber());
      }
      
      Console.WriteLine();
    } 
    
    finally 
    {
      Console.WriteLine("--> finally");
    }

    Console.ReadLine();
  }
}
]]>
</code>
</example>

<image>
<url>image/out_Excep6_smll.gif</url>
</image>

<image>
<url>image/out_Excep7_smll.gif</url>
</image>

</document>

<!-- Fin Excepciones -->




<document>
<tag>reflection</tag>
<title>Reflexión</title>



<text>
La capacidad de reflexión de la plataforma .NET (similar a la de 
la plataforma Java) nos permite explorar información sobre los tipos 
de los objetos en tiempo de ejecución.
</text>

<text>La instrucción <type>GetType()</type> obtiene el objeto 
<type>Type</type> de la instancia actual sobre el que se aplica. 
El valor devuelto es representa el tipo exacto, en tiempo de ejecución, 
de la instancia actual.
</text>

<image>
 <url>image/reflection.gif</url>
</image>


<example>
<title>Un sencillo ejemplo con Type y GetType()</title>
<code>
public class Test 
{
  private int n;

  public Test (int n) 
  { 
    this.n = n;
  }
}

// Acceso a información acerca de una clase

public static void Main(string[] args) 
{   
   Type tipoClase = typeof(Test);
   Console.WriteLine("El nombre del tipo de tipoClase es: {0}", 
                      tipoClase.Name);

   Test t = new Test(0);
   Type tipoVariable = t.GetType();
   Console.WriteLine("El tipo de la variable t es: {0}", 
                      tipoVariable.Name);
}
</code>
</example>


<text>El programa anterior muestra como resultado: </text>

<example>
<code>
El nombre del tipo de tipoClase es: Test
El tipo de la variable t es: Test
</code>
</example>


<text>Otro ejemplo del uso de <type>Type</type> y 
<type>GetType()</type>:</text>

<example>
<title>Un ejemplo más complejo con Type y GetType()</title>
<code>
<![CDATA[
Using System;

public class ClaseBase : Object {}

public class ClaseDerivada : ClaseBase {}

public class Test {

   public static void Main() {

      ClaseBase ibase = new ClaseBase();
      ClaseDerivada iderivada = new ClaseDerivada();
      Console.WriteLine("ibase:     Type is {0}", 
            ibase.GetType());
      Console.WriteLine("iderivada: Type is {0}", 
            iderivada.GetType());

      object o = iderivada;
      ClaseBase b = iderivada;
      Console.WriteLine("object o = iderivada: Type is {0}", 
            o.GetType());
      Console.WriteLine("ibase  b = iderivada: Type is {0}", 
            b.GetType());
      Console.ReadLine();
   }
}
]]>
</code>
</example>


<image>
 <url>image/reflectionType.gif</url>
</image>



<text>La reflexión puede emplearse para examinar los 
métodos, propiedades, ...  de una clase:</text>

<image>
 <url>image/MemberInfo.gif</url>
</image>


<example>
<title>Métodos de una clase</title>
<code>
<![CDATA[
using System;
using System.Reflection;

class ReflectApp
{
  public class Test 
  {
    private int n;

    public Test (int n) { 
      this.n = n;
    }
    public void Metodo1DeTest (int n) { 
      // .....
    }
    public int Metodo2DeTest (int a, float b, string c) { 
      // .....
      return 0;
    }
  }

  public static void Main(string[] args) 
  {
    Type t = typeof(Test);
  
    MethodInfo[] MetInf = t.GetMethods();
   
    foreach (MethodInfo m in MetInf) {
      Console.WriteLine ();
      Console.WriteLine ("Método: " + m.Name );
      Console.WriteLine ("  Características: " 
                  + ((m.IsPublic) ? " (public)" : "") 
                  + ((m.IsVirtual) ? " (virtual)" : ""));

      // Parámetros

      ParameterInfo[] ParInf = m.GetParameters();

      if (ParInf.Length > 0) {
        Console.WriteLine ("  Parámetros: " );
        foreach (ParameterInfo p in ParInf)
          Console.WriteLine("     " + p.ParameterType 
                      + " " + p.Name);
      }
    }
    Console.ReadLine ();
  }
}
]]>
</code>
</example>




<image>
 <url>image/reflectionMethods.gif</url>
</image>


</document>
<!-- Fin Reflexion -->



<document>
<tag>atribs</tag>
<title>Atributos</title>

<text>
Un <b>atributo</b> es <b>información</b> que se puede añadir a 
los metadatos de un módulo de código. 
Los atributos nos permiten "decorar" un elemento de 
nuestro código con información adicional. 
</text>

<text>
C# es un lenguaje imperativo, pero, como todos los lenguajes 
de esta categoría, contiene algunos elementos declarativos. 
Por ejemplo, la accesibilidad de un método de una clase se 
especifica mediante su declaración como <type>public</type>, 
<type>protected</type>, <type>private</type> o <type>internal</type>. 
C# generaliza esta capacidad permitiendo a los programadores 
inventar nuevas formas de información declarativa, 
anexarlas a distintas entidades del programa y recuperarlas 
en tiempo de ejecución. 
Los programas especifican esta información declarativa adicional 
mediante la definición y el uso de atributos.
</text>

<text>
Esta información puede ser referente tanto al propio módulo o el 
ensamblado al que peretenezca, como a los tipos de datos definidos 
en él, sus miembros, los parámetros de sus métodos, los bloques 
<type>set</type> y <type>get</type> de sus propiedades e indexadores 
o los bloques <type>add</type> y <type>remove</type> de sus eventos.
Se pueden emplear en ensamblados, módulos, tipos, miembros, 
valores de retorno y parámetros.
</text>


<document>
<title>Atributos predefinidos</title>

<text>
Si bien el programador puede definir cuantos atributos considere necesarios, algunos atributos ya están predefinidos en la plataforma .NET.
</text>

<dataset>
 <metadata>
  <field>Atributo</field>
  <field>Descripción</field>
 </metadata>
 <record>
  <field><type>Browsable</type></field>
  <field>Propiedades y eventos que deben mostrarse en el inspector de objetos.</field>
 </record>
 <record>
  <field><type>Serializable</type></field>
  <field>Clases y estructuras que pueden "serializarse" (esto es, volcarse en algún dispositivo de salida, p.ej. disco), como en Java.</field>
 </record>
 <record>
  <field><type>Obsolete</type></field>
  <field>El compilador se quejará si alguien los utiliza (<type>deprecated</type> en Java).</field>
 </record>
 <record>
  <field><type>ProgId</type></field>
  <field>COM Prog ID</field>
 </record>
 <record>
  <field><type>Transaction</type></field>
  <field>Características transaccionales de una clase.</field>
 </record>
</dataset>

<text>
Observar como al marcar como obsoleta la clase <type>A</type> se 
genera un error al compilar el módulo ya que se emplea en la línea 
comentada.
</text>

<example>
<code>
...

[Obsolete("Clase A desfasada. Usar B en su lugar")]
class A {
   public void F() {}
}
class B {
   public void F() {}
}

class SimpleAtrPredefApp
{

   static void Main(string[] args) 
   {

      A a = new A();       // Avisos
      a.F();
      ...
   }
}
</code>
</example>

<image>
 <url>image/obsolete.gif</url>
</image>

</document>
<!-- Atributos predefinidos -->


<document>
<title>Declarar una clase atributo</title>

<text>
Declarar un atributo en C# es simple: se utiliza la forma de una declaración 
de clase que hereda de <type>System.Attribute</type> y que se ha marcado con el 
atributo <type>AttributeUsage</type>, como se indica a continuación:
</text>

<example>
<code>
<![CDATA[
// La clase HelpAttribute posee un parámetro posicional (url) 
// de tipo string y un parámetro con nombre -opcional- (Topic) 
// de tipo string. 

[AttributeUsage(AttributeTargets.All)]
public class HelpAttribute: Attribute 
{
   public string Topic = null;
   private string url;

   public HelpAttribute(string url) 
   {
      this.url = url;
   }
   public string Url { 
      get { return url; }
   }
   public override string ToString() { 
      string s1 = "     Url = " +  this.Url;
      string dev = (this.Topic != null) ?
             (s1 + " - Topic: " + this.Topic) : s1;
      return (dev); 
   }
} // class HelpAttribute
]]>
</code>
</example>


<list>
<item>El atributo <type>AttributeUsage</type> especifica los elementos 
del lenguaje a los que se puede aplicar el atributo. </item>
<item>Las clases de atributos son clases públicas derivadas de 
<type>System.Attribute</type> que disponen al menos de un 
constructor público. </item>

<item>Las clases de atributos tienen dos tipos de parámetros: 
<list>
<item><i>Parámetros posicionales</i>, que se deben especificar cada vez que se 
utiliza el atributo. Los parámetros posicionales se especifican como argumentos 
de constructor para la clase de atributo. En el ejemplo anterior, 
<type>url</type> es un parámetro posicional. </item>
<item><i>Parámetros con nombre</i>, los cuales son opcionales. Si se especifican 
al usar el atributo, debe utilizarse el nombre del parámetro. Los parámetros con 
nombre se definen mediante un campo o una propiedad no estáticos. En el ejemplo 
anterior, <type>Topic</type> es un parámetro con nombre. </item>
</list>
</item>

<item>Los parámetros de un atributo sólo pueden ser valores constantes de los 
siguientes tipos: 

<list>
<item>Tipos simples (<type>bool</type>, <type>byte</type>, 
<type>char</type>, <type>short</type>, <type>int</type>, <type>long</type>, 
<type>float</type> y <type>double</type>) </item>
<item><type>string</type> </item>
<item><type>System.Type</type> </item>
<item>enumeraciones </item>
<item><type>object</type> (El argumento para un parámetro de atributo 
del tipo object debe ser un valor constante de uno de los 
tipos anteriores.) </item>
<item>Matrices unidimensionales de cualquiera de los tipos anteriores </item>
</list>
</item>

</list>

<text>
<b>Parámetros para el atributo AttributeUsage</b>
</text>

<text>
El atributo <type>AttributeUsage</type> proporciona el 
mecanismo subyacente mediante el cual los atributos 
se declaran. </text>

<text><type>AttributeUsage</type> tiene un parámetro posicional:</text> 
<list>
<item><type>AllowOn</type>, que especifica los elementos de programa 
a los que se puede asignar el atributo (clase, método, propiedad, 
parámetro, etc.). Los valores aceptados para este parámetro se pueden 
encontrar en la enumeración <type>System.Attributes.AttributeTargets</type> 
de .NET Framework. <b>El valor predeterminado para este parámetro es el de todos 
los elementos del programa</b> (<type>AttributeElements.All</type>).</item>
</list>

<text><type>AttributeUsage</type> tiene un parámetro con nombre:</text> 
<list>
<item><type>AllowMultiple</type>, valor booleano que indica si se 
pueden especificar varios atributos para un elemento de programa. 
El valor predeterminado para este parámetro es <type>False</type>. </item>
</list>

</document> <!-- Declarar una clase atributo -->



<document>
<title>Utilizar una clase atributo</title>

<text>
A continuación, se muestra un breve ejemplo de uso del 
atributo declarado en la sección anterior:
</text>

<example>
<code>
<![CDATA[
[Help("http://decsai.ugr.es/Clase1.htm")]
class Clase1 
{
   /* Bla, bla, bla... */
} 

[Help("http://decsai.ugr.es/Clase2.htm", Topic="Atributos")]
class Clase2 
{
   /* Bla, bla, bla... */
} 
]]>
</code>
</example>

<text>
En este ejemplo, el atributo <type>HelpAttribute</type> está asociado con las 
clases <type>Clase1</type> y <type>Clase2</type>.
</text>

<text>
<b>Nota:</b> Por convención, todos los nombres de atributo 
finalizan con la palabra "<type>Attribute</type>" para 
distinguirlos de otros elementos de .NET Framework. 
No obstante, no tiene que especificar el sufijo de atributo 
cuando utiliza atributos en el código (véase el ejemplo). 
</text>

</document> <!-- Utilizar una clase atributo -->



<document>
<title>Acceder a los atributos por reflexión</title>


<text>
Los atributos de un tipo o de un miembro de un tipo pueden ser 
examinados en tiempo de ejecución (<b>reflexión</b>), heredan 
de la clase <type>System.Attribute</type> y sus argumentos se 
comprueban en tiempo de compilación.
</text>

<text>
Los principales métodos de reflexión para consultar atributos se 
encuentran en la clase <type>System.Reflection.MemberInfo</type>. 
El método clave es <type>GetCustomAttributes</type>, que devuelve 
un vector de objetos que son equivalentes, en tiempo de ejecución, 
alos atributos del código fuente. 
</text>


<text><b>Ejemplo 1</b></text>

<text>
El siguiente ejemplo muestra la manera básica de 
utilizar la reflexión para obtener acceso a los atributos:
</text>

<example>
<code>
<![CDATA[
class AtributosSimpleApp 
{
   static void Main(string[] args) 
   {
      MemberInfo info1 = typeof(Clase1);
      object[] attributes1 = info1.GetCustomAttributes(true);
      for (int i = 0; i < attributes1.Length; i ++) {
         System.Console.WriteLine(attributes1[i]);
      }
      MemberInfo info2 = typeof(Clase2);
      object[] attributes2 = info2.GetCustomAttributes(true);
      for (int i = 0; i < attributes2.Length; i ++) {
         System.Console.WriteLine(attributes2[i]);
      }
      Console.ReadLine();

   } // Main ()
} // class AtributosSimpleApp 
]]>
</code>
</example>


<image>
 <url>image/out_Atr1.gif</url>
</image>


<text><b>Ejemplo 2</b></text>

<text>
Este ejemplo amplía el anterior añadiendo muchas más posibilidades.
</text>


<example>
<title>Atributos y reflexión</title>
<code>
<![CDATA[
using System;
using System.Diagnostics;
using System.Reflection;

// La clase IsTested es una clase de atributo 
// definida por el usuario. 
// Puede aplicarse a cualquier definición, incluyendo:  
//  - tipos (struct, class, enum, delegate)
//  - miembros (métodos, campos, events, properties, indexers)
// Se usa sin argumentos. 

[AttributeUsage(AttributeTargets.All)]
public class IsTestedAttribute : Attribute {
   public override string ToString() { return ("      REVISADO"); }
}

// La clase HelpAttribute posee un parámetro posicional (url) 
// de tipo string y un parámetro con nombre -opcional- (Topic) 
// de tipo string. 

[AttributeUsage(AttributeTargets.All)]
public class HelpAttribute: Attribute {
   public string Topic = null;
   private string url;

   public HelpAttribute(string url) {
      this.url = url;
   }
   public string Url { 
      get { return url; }
   }
   public override string ToString() 
   { 
      string s1 = "     Url = " +  this.Url;
      string dev = (this.Topic != null) ? 
         (s1 + ". Topic = " + this.Topic) : s1; 
      return (dev); 
   }

}

// La clase CodeReviewAttribute es una clase de atributo 
// definida por el usuario. 
// Puede aplicarse en clases y structs únicamente. 
// Toma dos argumentos de tipo string (el nombre del 
// revisor y la fecha de revisión) además de permitir otro 
// argumento opcional (Comment) de tipo string. 

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class CodeReviewAttribute: System.Attribute 
{
   public CodeReviewAttribute(string reviewer, string date) {
      this.reviewer = reviewer;
      this.date = date;
      this.comment = "";
   }
   
   public string Comment {
      get { return(comment); }
      set { comment = value; }
   }
   
   public string Date {
      get { return(date); }
   }

   public string Reviewer {
      get { return(reviewer); }
   }

   public override string ToString() 
   {
      string st1 = "     Revisor : " + Reviewer + "\n";
      string st2 = "     Fecha: " + Date; 
      string st3; 
      if (Comment.Length != 0)
         st3 = "\n" + "     NOTAS: " + Comment;
      else st3 = "";
      return (st1 + st2 + st3);
   }

   string reviewer;
   string date;
   string comment;
}

[CodeReview("Pepe", "01-12-2002", Comment="Codigo mejorable")]
[Help("http://decsai.ugr.es/Clase1.htm")]
[IsTested]
class Clase1 
{
   int c1c1;
   int c2c1;

   public Clase1 (int n1, int n2) 
   {
      this.c1c1 = n1;
      this.c2c1 = n2;
   }

   [IsTested]
   public override string ToString() 
   {
      return (this.c1c1.ToString() + this.c2c1.ToString()); 
   }   
}

[CodeReview("Juani", "12-11-2002", Comment="Excelente")]
[Help("http://decsai.ugr.es/Clase3.htm", Topic="Atributos")]
class Clase2 
{
   string c1c2;

   public Clase2 (string s) {
      this.c1c2 = s;
   }

   [IsTested]
   public char Met1Clase2 () 
   {
      return (this.c1c2[0]);
   }
}

[CodeReview("Pepe", "12-11-2002"), IsTested()]
class Clase3 
{
    int c1c3;

   [IsTested]
   public Clase3 (int n1) {
      this.c1c3 = n1;
   }
}

class Atributos1App
{
   private static bool IsMemberTested (MemberInfo member) 
   {
      foreach (object attr in member.GetCustomAttributes(true)) 
         if (attr is IsTestedAttribute) return true;
      return false;
   }

   private static string InfoRevision (MemberInfo member) 
   {
      if (IsMemberTested(member)) return ("REVISADO");
      else return ("NO REVISADO");
   }

   private static void DumpAttributes(MemberInfo member) 
   {
      Console.WriteLine();
      Console.WriteLine("Información de: " + member.Name);
/*
      object[] arr = 
         member.GetCustomAttributes(typeof(HelpAttribute), true);
      if (arr.Length == 0)
         Console.WriteLine("Esta clase no tiene ayuda.");
      else {
         HelpAttribute ha = (HelpAttribute) arr[0];
         Console.WriteLine (ha.ToString());
      }
*/
      foreach (object attribute in member.GetCustomAttributes(true)) 
      {
         if (attribute is HelpAttribute) 
            Console.WriteLine ("  Atributos de ayuda:");
         if (attribute is CodeReviewAttribute) 
            Console.WriteLine ("  Atributos de Revisión:");
         if (attribute is IsTestedAttribute) 
            Console.WriteLine ("  Atributos de Actualización:");

         Console.WriteLine(attribute);
      }
   }

   static void Main(string[] args)
   {
      // t es un vector de tipos 
      Type [] t = new Type[3];

      t[0] = typeof(Clase1); 
      t[1] = typeof(Clase2); 
      t[2] = typeof(Clase3); 

      for (int i=0; i<3; i++) {

         DumpAttributes(t[i]);

         Console.WriteLine ("  Información de los métodos:"); 

         foreach (MethodInfo m in (t[i]).GetMethods()) {
            if (IsMemberTested(m)) {
               Console.WriteLine("      Método {0} REVISADO", 
                  m.Name);
            }
            else {
               Console.WriteLine("      Método {0} NO REVISADO", 
                  m.Name);
            }
         }

      }
      Console.ReadLine();
   }
}
]]>
</code>
</example>


<image>
 <url>image/attr-reflect.gif</url>
</image>



</document> <!-- Acceder a los atributos por reflexión -->



</document>
<!-- Fin Atributos -->

</document> 
