<?xml version="1.0"  encoding="ISO-8859-1" ?> <!-- encoding="windows-1252" ?> -->
<?xml-stylesheet type="text/xsl" href="../csharp.xsl"?>

<document>
<title>Clases (2)</title>
<author>Fernando Berzal Galiano &amp; Francisco Cortijo Bon</author>

<navigation>
 <prev>
  <ref>inherit.xml</ref>
  <title>Herencia</title>
 </prev>
 <next>
   <ref>control.xml</ref>
   <title>Estructuras de control en C#</title>
 </next>
</navigation>

<!-- Indexadores -->

<document>
<tag>indexers</tag>
<title>Indexadores (indexers)</title>


<text>
C# no permite, hablado con rigor, la sobrecarga del operador de 
acceso a tablas <type>[ ]</type>. Si permite, no obstante, definir 
lo que llama un <i>indexador</i> para una clase que permite la 
misma funcionalidad. 
</text>

<text>
Los indexadores permiten definir código a ejecutar cada 
vez que se acceda a un objeto del tipo del que son miembros 
usando la sintaxis propia de las tablas, ya sea para leer 
o escribir. 

Esto es especialmente útil para hacer más clara la sintaxis 
de acceso a elementos de objetos que puedan contener 
<i>colecciones</i> de elementos, pues permite 
tratarlos como si fuesen tablas normales.
</text>

<text>
A diferencia de las tablas, los índices que se les pase entre 
corchetes no tiene porqué ser enteros, pudiéndose definir varios 
indexadores en un mismo tipo siempre y cuando cada uno tome 
un número o tipo de índices diferente. 
</text>

<text>
La sintaxis empleada para la definición de un 
indexador es similar a la de la definición de una propiedad. 
</text>

<example>
<code>
public class MiClase
{
   ...
   public string this[int x] 
   {
     get { 
        // Obtener un elemento 
     }
     set { 
        // Fijar un elemento 
     }
   }
   ...
}
...
MiClase MiObjeto = new MiClase();
</code>
</example>

<text>
El código que aparece en el bloque <type>get</type> se ejecuta 
cuando la expresión <type>MiObjeto[x]</type> aparece en la parte 
derecha de una expresión mientras que el código que aparece en 
el bloque <type>set</type> se ejecuta cuando <type>MiObjeto[x]</type> 
aparece en la parte izquierda de una expresión.
</text>

<text>Algunas consideraciones finales: 
<list>
<item>
Igual que las propiedades, pueden ser de sólo lectura, 
de sólo escritura o de lectura y escritura.
</item>
<item>El nombre dado a un indexador siempre ha de ser 
<type>this</type>. 
</item>
<item>
Lo que diferenciará a unos indexadores de otros será el 
número y tipo de sus índices.
</item>
</list>
</text>

<!-- <text><b>Ejemplo</b></text> -->

<example>
<title>Ejemplo de indexador</title>
<code>
<![CDATA[
using System;

namespace IndexadorCoches
{
   public class Coche
   {
      private int VelocMax;
      private string Marca;  
      private string Modelo;

      public Coche (string marca, string modelo, int velocMax)
      {
         this.VelocMax = velocMax; 
         this.Marca = marca;
         this.Modelo = modelo;
      }

      public override string ToString () {
            return (this.Marca + " " + this.Modelo + 
                    "  (" + this.VelocMax + " Km/h)");
      }

   } // class Coche


   public struct DataColeccionCoches  
   {
      public int numCoches;
      public int maxCoches;
      public Coche[] vectorCoches;

      public DataColeccionCoches (int max)
      {
         this.numCoches = 0; 
         this.maxCoches = max; 
         this.vectorCoches = new Coche[max];
      }
   } // struct DataColeccionCoches

   public class Coches 
   {
      // Los campos se encapsulan en un struct 
      DataColeccionCoches data;

      // Constructor sin parámetros
      public Coches()
      {
         this.data = new DataColeccionCoches(10);
         // Si no se pone un valor se llamará al 
         // constructor sin parámetros (por defecto) del 
         // struct y tendremos problemas: no se puede 
         // explicitar el constructor sin parámetros 
         // para un struct.
      }

      // Constructor con parámetro
      public Coches(int max)
      {
         this.data = new DataColeccionCoches(max);
      }

      public int MaxCoches 
      {
         set { data.maxCoches = value; }
         get { return (data.maxCoches); }
      }
      public int NumCoches 
      {
         set { data.numCoches = value; }
         get { return (data.numCoches); }
      }
      public override string ToString () {
         string str1 = "  --> Maximo= " + this.MaxCoches; 
         string str2 = "  --> Real  = " + this.NumCoches;
         return (str1 + "\n" + str2);
      }

      // El indexador devuelve un objeto Coche de acuerdo 
      // a un índice numérico 
      public Coche this[int pos]
      {
         // Devuelve un objeto del vector de coches
         get
         {
            if(pos < 0 || pos >= MaxCoches)
               throw new 
                  IndexOutOfRangeException("Fuera de rango");
            else
               return (data.vectorCoches[pos]);
         }
         // Escribe en el vector 
         set { this.data.vectorCoches[pos] = value;}
      }

   } // class Coches

   class IndexadorCochesApp
   {
      static void Main(string[] args)
      {
         // Crear una colección de coches 
         Coches MisCoches = new Coches (); // Por defecto (10)

         Console.WriteLine ("***** Mis Coches *****"); 
         Console.WriteLine ("Inicialmente: "); 
         Console.WriteLine (MisCoches); 


         // Añadir coches. Observar el acceso con [] ("set") 
         MisCoches[0] = new Coche ("Opel", "Zafira", 200);
         MisCoches[1] = new Coche ("Citröen", "Xsara", 220);
         MisCoches[2] = new Coche ("Ford", "Focus", 190);

         MisCoches.NumCoches = 3;

         Console.WriteLine ("Despues de insertar 3 coches: ");
         Console.WriteLine (MisCoches); 


         // Mostrar la colección 
         Console.WriteLine ();
         for (int i=0; i<MisCoches.NumCoches; i++) 
         {
            Console.Write ("Coche Num.{0}: ", i+1); 
            Console.WriteLine (MisCoches[i]); // Acceso ("get")
         }
         Console.ReadLine ();

         // *********************************************

         // Crear una colección de coches 
         Coches TusCoches = new Coches (4);

         Console.WriteLine ("***** Tus Coches *****"); 
         Console.WriteLine ("Inicialmente: "); 
         Console.WriteLine (TusCoches); 

         // Añadir coches. Observar el acceso con []
         TusCoches[TusCoches.NumCoches++] = 
               new Coche ("Opel", "Corsa", 130);
         TusCoches[TusCoches.NumCoches++] = 
               new Coche ("Citröen", "C3", 140);

         Console.WriteLine ("Despues de insertar 2 coches: ");
         Console.WriteLine (TusCoches); 

         // Mostrar la colección 
         Console.WriteLine ();
         for (int i=0; i<TusCoches.NumCoches; i++) 
         {
            Console.Write ("Coche Num.{0}: ", i+1); 
            Console.WriteLine (TusCoches[i]); // Acceso ("get")
         }

         Console.ReadLine ();

      } // Main

   } // class IndexadorCochesApp

} // namespace IndexadorCoches
]]>
</code>
</example>

<image>
<url>image/out-Index1.gif</url>
</image>


</document>  <!-- Indexadores -->




<!-- Interfaces -->

<document>
<tag>interface</tag>
<title>Interfaces</title>

<text>
Un interfaz define un <b>contrato</b> semántico que 
ha de respetar cualquier clase (o <type>struct</type>) 
que implemente el interfaz.
</text>

<text>La interfaz no contiene implementación alguna.  
La clase o struct que implementa el interfaz es la 
que tiene la funcionalidad especificada por el 
interfaz.</text>

<text>Una interfaz puede verse como una forma especial de 
definir clases que sólo cuenten con miembros abstractos. 
Sin embargo, todo tipo que derive de una interfaz ha de dar 
una implementación de todos los miembros que hereda de esta, 
y no como ocurre con las clases abstractas donde es posible no 
darla si se define como abstracta también la clase hija. 
</text>

<list>

<item>La especificación del interfaz puede incluir métodos, 
propiedades, indexadores y eventos, pero no campos, 
operadores, constructores o destructores.</item>

<item>Aunque solo se permite la <b>herencia simple 
de clases</b>, como ocurre en Java, se permite 
y <b>herencia múltiple de interfaces</b>.
Esto significa que es posible definir tipos que deriven de 
más de una interfaz. 
</item>

<item>Los interfaces (como algo separado de la 
implementación) permiten la existencia del 
polimorfismo, al poder existir muchas clases o 
structs que implementen el interfaz.</item>

</list>


<example>
<title>Ejemplo de polimorfismo</title>
<code>
<![CDATA[
using System;

namespace Interface1
{
  class Interface1App
  {
    // Definición de una interface 

    public interface IDemo 
    {
      void MetodoDeIDemo ();
    }

    // "Clase1" y "Clase2" implementan la interface 

    public class Clase1 : IDemo
    {
      public void MetodoDeIDemo() 
      { 
        Console.WriteLine ("Método de Clase1");
      }
    }

    public class Clase2 : IDemo
    {
      public void MetodoDeIDemo()   
      { 
        Console.WriteLine ("Método de Clase2");
      }
    }
    
    static void Main(string[] args)
    {
      Clase1 c1 = new Clase1();
      Clase2 c2 = new Clase2();
      IDemo demo;  // objeto de una interface
      
      // Ejemplo de polimorfismo 
      demo = c1;
      demo.MetodoDeIDemo();

      demo = c2;
      demo.MetodoDeIDemo();

      Console.ReadLine(); 
    }
  }
}
]]>
</code>
</example>

<image>
<url>image/out-Inter1.gif</url>
</image>


<text>Otro ejemplo:</text>
<example>
<title>Ejemplo de interface "heredada"</title>
<code>
<![CDATA[
using System;

class InterfaceApp
{

  interface IPresentable
  {
    void Presentar();
  }

  class Triangulo : IPresentable
  {
    private double b, a; 
    public Triangulo(double Base, double altura)
    {
      this.b=Base;
      this.a=altura;
    }
    public double Base
    {
      get { return b; }
    }
    public double Altura
    {
      get { return a; }
    }
    public double Area
    {
      get { return (Base*Altura/2); }
    }
    public void Presentar()
    {
      Console.WriteLine("Base del triángulo: {0}", Base);
      Console.WriteLine("Altura del triángulo: {0}", Altura);
      Console.WriteLine("Área del triángulo: {0}", Area);
    }
  }
  class Persona : IPresentable
  {
    private string nbre, apell, dir; 
    public Persona (string nombre, string apellidos, 
                    string direccion)
    {
      this.nbre = nombre;
      this.apell = apellidos;
      this.dir = direccion;
    }
    public string Nombre
    {
      get { return nbre; }
    }
    public string Apellidos
    {
      get { return apell; }
    }
    public string Direccion
    {
      get { return dir; }
    }
    public void Presentar()
    {
      Console.WriteLine("Nombre: {0}", Nombre);
      Console.WriteLine("Apellidos: {0}", Apellidos);
      Console.WriteLine("Dirección: {0}", Direccion);
    }
  }

  static void VerDatos(IPresentable IP)
  {
    IP.Presentar();
  }

  static void Main(string[] args)
  {
    Triangulo t=new Triangulo(10,5);
    Persona p=new Persona ("Paco", "Pérez", "su casa");

    Console.WriteLine("Ya se han creado los objetos");
    Console.WriteLine("\nINTRO para VerDatos(triangulo)");
    Console.ReadLine();
    VerDatos(t);

    Console.WriteLine("\nINTRO para VerDatos(proveedor)");
    Console.ReadLine();
    VerDatos(p);  

    Console.ReadLine();
  }
}
]]>
</code>
</example>

<image>
<url>image/out-Inter3.gif</url>
</image>


<text>
El principal uso de las interfaces es indicar que una 
clase implementa ciertas características. Por ejemplo, 
el ciclo <type>foreach</type> trabaja internamente 
comprobando que la clase sobre la que se aplica 
implementa el interfaz <type>IEnumerable</type> y 
llamando a los métodos definidos en esa interfaz. 
</text>


<document>
<title>Herencia múltiple</title>

<text>
La plataforma .NET no permite herencia múltiple de 
implementación, aunque sí se puede conseguir 
<b>herencia múltiple de interfaces</b>. 
Clases, structs e interfaces pueden heredar de 
múltiples interfaces (como en Java).
</text>

<example>
<title>Herencia de interfaces</title>
<code>
<![CDATA[
using System;

namespace Interface2
{
  class Interface2App
  {
    // Definición de interfaces 

    public interface IDemo1 
    {
      void Metodo1DeInterface1 ();
      string Metodo2DeInterface1 ();
    }

    public interface IDemo2 
    {
      void Metodo1DeInterface2 ();
    }

    public interface IDemo3 : IDemo1
    {
      void Metodo1DeInterface3 (string mensaje);
    }

    // "Clase1" implementan la interface "IDemo1"

    public class Clase1 : IDemo1
    {
      public void Metodo1DeInterface1() 
      { Console.WriteLine ("Mét1 de Int1 en Clase1"); }

      public string Metodo2DeInterface1() 
      { return ("En Mét2 de Int1 en Clase1"); }

    }

    // "Clase1" implementan las interfaces 
    //    "IDemo1" e "IDemo2"

    public class Clase2 : IDemo1, IDemo2
    {
      public void Metodo1DeInterface1() 
      { Console.WriteLine ("Mét1 de Int1 en Clase2"); }

      public string Metodo2DeInterface1() 
      { return ("En Mét2 de Int1 en Clase2"); }

      public void Metodo1DeInterface2()   
      { Console.WriteLine ("Mét1 de Int2 en Clase2"); }
    }

    // "Clase3" implementan la interface "IDemo3", la 
    //    cual ha heredado de "IDemo1"

    public class Clase3 : IDemo3
    {
       public void Metodo1DeInterface1()   
      { Console.WriteLine ("Mét1 de Int1 en Clase3"); }

      public string Metodo2DeInterface1() 
      { return ("En Mét2 de Int1 en Clase3"); }

      public void Metodo1DeInterface3 (string m)   
      { Console.WriteLine (m + "Mét1 de Int3 en Clase3"); }

    }
    static void Main(string[] args)
    {
      Clase1 c1 = new Clase1();
      Clase2 c2 = new Clase2();
      Clase3 c3 = new Clase3();

      IDemo1 i1;  
      IDemo2 i2;  
      IDemo3 i3;  
    
      c1.Metodo1DeInterface1();
      Console.WriteLine(c1.Metodo2DeInterface1());
      Console.WriteLine();

      i1 = c3;
      Console.WriteLine("Cuando i1 = c3 ");
      i1.Metodo1DeInterface1();
      Console.WriteLine(i1.Metodo2DeInterface1());
      Console.WriteLine();

      i3 = c3;
      Console.WriteLine("Cuando i3 = c3 ");
      i3.Metodo1DeInterface1();
      Console.WriteLine(i3.Metodo2DeInterface1());
      i3.Metodo1DeInterface3("Aplicado a i3: ");
      Console.WriteLine();

      i1 = c2;
      Console.WriteLine("Cuando i1 = c2 ");
      i1.Metodo1DeInterface1();
      Console.WriteLine(i1.Metodo2DeInterface1());
      i2 = c2;
      Console.WriteLine("Ahora i2 = c2 ");
      i2.Metodo1DeInterface2();
      Console.WriteLine();

      Console.ReadLine(); 
    }
  }
}
]]>
</code>
</example>

<image>
<url>image/out-Inter2.gif</url>
</image>

<document>
<title>Resolución de conflictos de nombres</title>

<text>
Si dos interfaces tienen un método con el mismo nombre, 
se especifica explícitamente el interfaz al que 
corresponde la llamada al método para eliminar ambigüedades
</text>

<example>
<code>
interface IControl 
{
  void Delete();
}

interface IListBox: IControl 
{
  void Delete();
}

interface IComboBox: ITextBox, IListBox 
{
  void IControl.Delete();
  void IListBox.Delete();
}
</code>
</example>

</document>

</document>

</document>




<document>
<tag>delegate</tag>
<title>Delegados</title>


<text>
Un <b>delegado</b> es <i>un tipo especial de clase 
que define la signatura de un método</i>. 
Su función es similar a la de los punteros a funciones 
en lenguajes como C y C++ (C# no soporta punteros a 
funciones). 
</text>

<text>
Los delegados pueden pasarse a métodos y pueden usarse 
para llamar a los métodos de los que contienen 
referencias. 
</text>

<text>
Los delegados proporcionan polimorfismo 
para las llamadas a funciones:
</text>


<example>
<title>Un "tipo" delegado</title>
<code>
<![CDATA[
using System;

class Delegate1App
{
  // Declaración del "tipo delegado" llamado 
  //   "Del": funciones que devuelven un double 
  //   y reciben un double. 
  delegate double Del (double x);   

  static double incremento (double x) 
  { return (++x); }

  static void Main(string[] args)
  {
  	// Instanciación 
  	Del del1 = new Del (Math.Sin);  
  	Del del2 = new Del (Math.Cos);  
  	Del del3 = new Del (incremento);  
  	
  	// Llamadas
  	Console.WriteLine (del1(0));  //  0
  	Console.WriteLine (del2(0));  //  1
  	Console.WriteLine (del3(10)); // 11

  	Console.ReadLine();
  }
}
]]>
</code>
</example>

<text>
Para hacer más evidente el polimorfismo 
el método <type>Main</type> podría escribirse como: 
</text>

<example>
<code>
<![CDATA[
  static void Main(string[] args)
  {
    Del f1 = new Del (Math.Sin); 
    Del f2 = new Del (Math.Cos); 			
    Del f3 = new Del (incremento); 

    Del f; 
    f = f1; Console.WriteLine (f(0));  //  0
    f = f2; Console.WriteLine (f(0));  //  1
    f = f3; Console.WriteLine (f(10)); // 11			
  }
]]>
</code>
</example>

<text>
o bien así:</text>

<example>
<code>
<![CDATA[
  static void Main(string[] args)
  {
    Del[] d = new Del[3];
    
    d[0] = new Del (Math.Sin);
    d[1] = new Del (Math.Cos);			
    d[2] = new Del (incremento); 

    for (int i=0; i<2; i++) 
       Console.WriteLine (d[i](0));  //  0, 1
    Console.WriteLine (d[2](10));  //  11
  }
]]>
</code>
</example>

<text>
Los delegados son muy útiles ya que permiten disponer de 
objetos cuyos métodos puedan ser modificados dinámicamente 
durante la ejecución de un programa. 
</text>

<text>
En general, son útiles en todos aquellos casos en que 
interese pasar métodos como parámetros de otros métodos.
</text>

<text>
Los delegados son la base sobre la que se monta la 
gestión de eventos en la plataforma .NET.
</text>


<document>
<title>Multicasting</title>

<text>
Un delegado es un tipo especial de clase cuyos objetos 
pueden almacenar referencias a uno o más métodos, de tal 
manera que a través del objeto sea posible solicitar 
la ejecución en cadena de todos ellos. 
</text>

<text>
Un delegado puede contener e invocar múltiples métodos. 
De esta forma se puede hacer multicasting de una forma 
sencilla y elegante. Para que un delegado pueda contener 
varios métodos, éstos no pueden devolver ningún valor 
(si lo intentasen devolver, se generaría una excepción 
en tiempo de ejecución). 
</text>

<example>
<title>"Multicasting"</title>
<code>
<![CDATA[
using System;

class Delegate2App
{
  delegate void SomeEvent (int x, int y);

  static void Func1(int x, int y) 
  { Console.WriteLine("  Desde Func1"); }

  static void Func2(int x, int y) 
  { Console.WriteLine("  Desde Func2"); }

  static void Main(string[] args)
  {
    SomeEvent func = new SomeEvent(Func1);

    func += new SomeEvent(Func2);

    Console.WriteLine("Llamada a func");
    func(1,2);  // Se llama tanto a Func1 como a Func2

    func -= new SomeEvent(Func1);

    Console.WriteLine("Llamada a func");
    func(2,3);  // Sólo se llama a Func2

    Console.ReadLine();
  }
}
]]>
</code>
</example>

<image>
<url>image/out-Del1.gif</url>
</image>

<text>
Cada delegado tiene una lista ordenada de métodos 
que se invocan secuencialmente (en el mismo orden 
en el que fueron añadidos al delegado). 
Los operadores += y -= se utilizan para añadir 
y eliminar métodos de la lista asociada a un delegado.
</text>

</document>

<document>
<title>Delegados vs. interfaces</title>

<text>
Siempre se pueden utilizar interfaces en vez de 
delegados. Los interfaces son más versátiles, 
ya que pueden encapsular varios métodos y permiten 
herencia, si bien los delegados resultan más 
adecuados para implementar manejadores de eventos. 
Con los delegados se escribe menos código y se 
pueden implementar fácilmente múltiples manejadores 
de eventos en una única clase.
</text>

</document>


<document>
<title>Eventos</title>

<text>
Muchas aplicaciones actuales se programan en función de eventos. 
Cuando se produce algún hecho de interés para nuestra 
aplicación, éste se notifica mediante la generación 
de un evento, el cual será procesado por el manejador 
de eventos correspondiente (modelo "publish-subscribe"). 
Los eventos nos permiten enlazar código personalizado 
a componentes creados previamente (mecanismo de "callback").
</text>

<text>
El <i>callback</i> consiste en que un  cliente notifica a 
un servidor que desea ser informado cuando alguna acción 
tenga lugar. C# usa los eventos de la misma manera que 
Visual Basic usa los mensajes. 
</text>

<text>
Las aplicaciones en Windows se programan utilizando 
eventos, pues los eventos resultan especialmente 
indicados para la implementación de interfaces 
interactivos. Cuando el usuario hace algo (pulsar 
una tecla, hacer click con el ratón, seleccionar un 
dato de una lista...), el programa reacciona en 
función de la acción del usuario.
</text>

<text>
El uso de eventos, no obstante, no está limitado 
a la implementación de interfaces. También son útiles 
en el desarrollo de aplicaciones que deban realizar 
operaciones periódicamente o realizar operaciones 
de forma asíncrona (p.ej. llegada de un correo 
electrónico, terminación de una operación larga...).
</text>

<text>
El lenguaje C# da soporte a los eventos mediante el 
uso de delegados. Al escribir nuestra aplicación, 
un evento no será más que un campo que almacena un 
delegado. Los usuarios de la clase podrán registrar 
delegados (mediante los operadores += y -=), pero 
no podrán invocar directamente al delegado.
</text>

<example>
<title>Eventos en C#</title>
<code>
public delegate void EventHandler ( object sender, EventArgs e);


public class Button 
{
  public event EventHandler Click;

  protected void OnClick (EventArgs e) 
  {
    // This is called when button is clicked
    if (Click != null) Click(this, e);
  }
}


public class MyForm: Form 
{
  Button okButton;

  static void OkClicked(object sender, EventArgs e) 
  {
    ShowMessage("You pressed the OK button");
  }

  public MyForm() 
  {
    okButton = new Button(...);
    okButton.Caption = "OK";
    okButton.Click += new EventHandler(OkClicked);
  }
}
</code>
</example>


</document>

</document> <!-- Delegados -->



</document>
