Técnicas avanzadas de C# (C# y Java)

Actualización: noviembre 2007

C# proporciona algunas características de lenguaje útiles, como los indizadores, atributos y delegados, que permiten emplear técnicas de programación avanzadas.

Indizadores

Los indizadores proporcionan una forma de tener acceso a class o struct de la misma manera que a una matriz. Por ejemplo, se puede utilizar una clase que representa un departamento único en una compañía. La clase podría contener los nombres de todos los empleados del departamento y los indizadores podrían permitir el acceso a estos nombres, del siguiente modo:

sales[0] = "Nikki";
sales[1] = "Becky";

Los indizadores se habilitan definiendo una propiedad con la firma siguiente, por ejemplo, en la definición de clase:

public string this [int index]  //indexer

A continuación, se proporcionan los métodos get y set como se haría para una propiedad normal. Estos descriptores de acceso son los que especifican a qué miembro interno se hace referencia cuando se utiliza el indizador.

En el siguiente ejemplo, se crea una clase denominada Department que utiliza indizadores para tener acceso a los empleados de ese departamento, representados internamente como una matriz de cadenas:

public class Department
{
    private string name;
    private const int MAX_EMPLOYEES = 10;
    private string[] employees = new string[MAX_EMPLOYEES];  //employee array

    public Department(string departmentName)  //constructor
    {
        name = departmentName;
    }

    public string this [int index]  //indexer
    {
        get
        {
            if (index >= 0 && index < MAX_EMPLOYEES)
            {
                return employees[index];
            }
            else
            {
                throw new System.IndexOutOfRangeException();
            }
        }
        set
        {
            if (index >= 0 && index < MAX_EMPLOYEES)
            {  
                employees[index] = value;
            }
            else
            {
                throw new System.IndexOutOfRangeException();
            }
        }
    }

    // code for the rest of the class...
}

A continuación, puede crear una instancia de esta clase y tener acceso a ella como se muestra en el ejemplo de código siguiente:

class TestDepartment
{
    static void Main()
    {
        Department sales = new Department("Sales");

        sales[0] = "Nikki";
        sales[1] = "Becky";

        System.Console.WriteLine("The sales team is {0} and {1}", sales[0], sales[1]);
    }
}

El resultado es:

The sales team is Nikki and Becky

Para obtener más información, vea Indizadores (Guía de programación de C#).

Atributos

C# proporciona un mecanismo, denominado atributo, para agregar información declarativa sobre los tipos. Los atributos son de algún modo similares al concepto de anotaciones en Java. La información adicional sobre un tipo se coloca dentro de etiquetas declarativas que preceden la definición de tipo. En los ejemplos siguientes se muestra cómo utilizar los atributos de .NET Framework para decorar una clase o un método.

En el ejemplo siguiente, el método GetTime se marca como servicio Web XML al agregar el atributo WebMethodAttribute.

public class Utilities : System.Web.Services.WebService
{
    [System.Web.Services.WebMethod]  // Attribute
    public string GetTime()
    {
        return System.DateTime.Now.ToShortTimeString();
    }
}

Agregar el atributo WebMethod provoca que .NET Framework se ocupe automáticamente del intercambio de XML/SOAP necesario para llamar a esta función. Al llamar a este servicio Web, se recupera el valor siguiente:

<?xml version="1.0" encoding="utf-8" ?>

<string xmlns="http://tempuri.org/">7:26 PM</string>

En el ejemplo siguiente, la clase Employee se marca como serializable al agregar el atributo SerializableAttribute. Mientras el campo Salary esté marcado como público, no se serializará puesto que está marcado con el atributo NonSerializedAttribute.

[System.Serializable()]        
public class Employee  
{
    public int ID;
    public string Name;        
    [System.NonSerialized()] public int Salary; 
}

Para obtener más información, vea Crear atributos personalizados (Guía de programación de C#).

Delegados

Los lenguajes como C++, Pascal y otros admiten el concepto de punteros a función, que le permiten elegir a qué función desea llamar en tiempo de ejecución.

Java no proporciona ninguna construcción con la funcionalidad de puntero a función, pero C# sí lo permite. A través del uso de la clase Delegate, una instancia de delegate encapsula un método que es una entidad invocable.

En el caso de los métodos de instancia, el delegado consta de una instancia de la clase contenedora y un método en la instancia. En los métodos estáticos, una entidad invocable consta de una clase y un método estático en la clase. Así, se puede utilizar un delegado para invocar una función de cualquier objeto y los delegados se orientan a objetos, tienen seguridad de tipos y son seguros.

Hay tres pasos para definir y utilizar los delegados:

  • Declaración

  • Creación de instancias

  • Invocación

Los delegados se declaran con la siguiente sintaxis:

delegate void Del1();

Este delegado se puede utilizar posteriormente para hacer referencia a cualquier función que devuelve un valor void y no toma ningún argumento.

Asimismo, a fin de crear un delegado para cualquier función que tome un parámetro de cadena y devuelva un valor long, utilizaría la sintaxis siguiente:

delegate long Del2(string s);

A continuación, podría asignar este delegado a cualquier método con esta firma, como:

Del2 d;                // declare the delegate variable
d = DoWork;  // set the delegate to refer to the DoWork method

Donde la firma de DoWork es:

public static long DoWork(string name)

Reasignar los delegados

Los objetos Delegate son inmutables; es decir, la firma con la que coinciden no se puede cambiar una vez establecida. Sin embargo, puede señalar a otro método siempre y cuando ambos tengan la misma firma. En este ejemplo, se reasigna d a un nuevo objeto de delegado para que d invoque el método DoMoreWork. Sólo se puede hacer esto si DoWork y DoMoreWork tienen la misma firma.

Del2 d;                    // declare the delegate variable
d = DoWork;      // set the delegate to refer to the DoWork method
d = DoMoreWork;  // reassign the delegate to refer to the DoMoreWork method

Invocar delegados

Invocar un delegado es bastante sencillo. Simplemente sustituya el nombre de la variable de delegado para el nombre de método. Esto invoca el método Add con los valores 11 y 22, y devuelve un resultado de tipo long que se asigna a la variable sum:

Del operation;                 // declare the delegate variable
operation = Add;      // set the delegate to refer to the Add method
long sum = operation(11, 22);  // invoke the delegate

En el ejemplo siguiente se muestra la generación, creación de instancias e invocación de un delegado:

public class MathClass
{
    public static long Add(int i, int j)       // static
    {
        return (i + j);
    }

    public static long Multiply (int i, int j)  // static
    {
        return (i * j);
    }
}

class TestMathClass
{
    delegate long Del(int i, int j);  // declare the delegate type

    static void Main()
    {
        Del operation;  // declare the delegate variable

        operation = MathClass.Add;       // set the delegate to refer to the Add method
        long sum = operation(11, 22);             // use the delegate to call the Add method

        operation = MathClass.Multiply;  // change the delegate to refer to the Multiply method
        long product = operation(30, 40);         // use the delegate to call the Multiply method

        System.Console.WriteLine("11 + 22 = " + sum);
        System.Console.WriteLine("30 * 40 = " + product);
    }
}

Resultado

11 + 22 = 33

30 * 40 = 1200

Una instancia de delegado debe contener una referencia de objeto. El ejemplo anterior soluciona esto al declarar los métodos como estáticos, lo que significa que no hay necesidad de especificar una referencia de objeto. Sin embargo, si un delegado hace referencia a un método de instancia, se debe proporcionar la referencia de objeto del modo siguiente:

Del operation;                   // declare the delegate variable
MathClass m1 = new MathClass();  // declare the MathClass instance
operation = m1.Add;     // set the delegate to refer to the Add method

En este ejemplo, Add y Multiply son métodos de instancia de MathClass. Si los métodos de MathClass no se declaran como estáticos, se los invoca a través del delegado mediante una instancia de MathClass, de la forma siguiente:

public class MathClass
{
    public long Add(int i, int j)       // not static
    {
        return (i + j);
    }

    public long Multiply (int i, int j)  // not static
    {
        return (i * j);
    }
}

class TestMathClass
{
    delegate long Del(int i, int j);  // declare the delegate type

    static void Main()
    {
        Del operation;                   // declare the delegate variable
        MathClass m1 = new MathClass();  // declare the MathClass instance

        operation = m1.Add;       // set the delegate to refer to the Add method
        long sum = operation(11, 22);      // use the delegate to call the Add method

        operation = m1.Multiply;  // change the delegate to refer to the Multiply method
        long product = operation(30, 40);  // use the delegate to call the Multiply method

        System.Console.WriteLine("11 + 22 = " + sum);
        System.Console.WriteLine("30 * 40 = " + product);
    }
}

Resultado

Este ejemplo proporciona el mismo resultado que el ejemplo anterior, en el que los métodos se declararon como estáticos.

11 + 22 = 33

30 * 40 = 1200

Delegados y eventos

.NET Framework también utiliza delegados en gran medida para las tareas de control de eventos, como un evento de clic de un botón en una aplicación para Windows o Web. Mientras que el control de eventos de Java se realiza generalmente implementando clases de agente de escucha personalizadas, los desarrolladores de C# pueden aprovechar los delegados para el control de eventos. event se declara como un campo con un tipo de delegado, sólo que la palabra clave event precede a la declaración del evento. Los eventos generalmente se declaran como public, pero se permite cualquier modificador de accesibilidad. En el ejemplo siguiente se muestra la declaración de delegate y event.

// Declare the delegate type:
public delegate void CustomEventHandler(object sender, System.EventArgs e);

// Declare the event variable using the delegate type:
public event CustomEventHandler CustomEvent;

Los delegados de evento son de multidifusión, lo que significa que pueden guardar referencias a más de un método de control de eventos. Un delegado actúa como remitente de eventos de la clase que provoca el evento y mantiene una lista de los controladores registrados para el evento. En el ejemplo siguiente se muestra cómo suscribir varias funciones a un evento. La clase EventClass contiene el delegado, el evento y un método para invocar el evento. Tenga en cuenta que un evento sólo se puede invocar desde la clase que lo declaró. La clase TestEvents se puede suscribir luego al evento mediante el operador += y la suscripción se puede cancelar con el operador -=. Cuando se llama al método InvokeEvent, éste desencadena el evento y las funciones que se hayan suscrito al evento se desencadenarán en forma sincrónica, como se muestra en el ejemplo siguiente.

public class EventClass
{
    // Declare the delegate type:
    public delegate void CustomEventHandler(object sender, System.EventArgs e);

    // Declare the event variable using the delegate type:
    public event CustomEventHandler CustomEvent;

    public void InvokeEvent()
    {
        // Invoke the event from within the class that declared the event:
        CustomEvent(this, System.EventArgs.Empty);
    }
}

class TestEvents
{
    private static void CodeToRun(object sender, System.EventArgs e)
    {
        System.Console.WriteLine("CodeToRun is executing");
    }

    private static void MoreCodeToRun(object sender, System.EventArgs e)
    {
        System.Console.WriteLine("MoreCodeToRun is executing");
    }

    static void Main()
    {
        EventClass ec = new EventClass();

        ec.CustomEvent += new EventClass.CustomEventHandler(CodeToRun);
        ec.CustomEvent += new EventClass.CustomEventHandler(MoreCodeToRun); 

        System.Console.WriteLine("First Invocation:");
        ec.InvokeEvent();

        ec.CustomEvent -= new EventClass.CustomEventHandler(MoreCodeToRun);

        System.Console.WriteLine("\nSecond Invocation:");
        ec.InvokeEvent();
    }
}

Resultado

First Invocation:

CodeToRun is executing

MoreCodeToRun is executing

Second Invocation:

CodeToRun is executing

Vea también

Tareas

Ejemplo de delegados

Conceptos

Guía de programación de C#

Referencia

Delegados (Guía de programación de C#)

Eventos (Guía de programación de C#)

Otros recursos

Lenguaje de programación C# para desarrolladores de Java