Hoy vamos a ver unos objetos y métodos algo especiales. Todo esta explicado en el código. Por un lado, los indexadores
/**
* Indexadores.cs
* Los indexadores sirven para acceder a un array que puede haber dentro
* de la clase de forma directa. Supongamos que tenemos un atributo que es un
* array de nombres: protected string[] nombres;
* Con el indexador o indexer accederiamos a ese array así:
*   MiClase clase = new MiClase();
*   clase[0] = "Nuevo valor";
* Y para eso hay que definir un indexer.
*/

// Vamos a definir un equipo de fútbol
public class Equipo
{
	// Definimos un atributo que contiene los jugadores del equipo
	private string[] jugadores = new string[5];
	// Un atributo para guardar el nombre del equipo
	private string nombreEquipo;

	// Constructor
	public Equipo(string nombre)
	{
		nombreEquipo = nombre;
	}

	// método que devuelve tamaño
	public int getSize()
	{
		return jugadores.Length;
	}

	// Indexador
	public string this[int indice]
	{
		get // Mediante el get podemos sacar un valor del array de jugadores
		{
			string temp;
			// Comprobamos indices no nos vayamos a salir
			if( indice >= 0 && indice <= 4 )
			{
 				temp = jugadores[indice];
			}
 			else
 			{
 				temp = "";
			}
			return temp;
		}

	  set // Mediante el set podemos establecer un valor en el array de jugadores
 	  {
	 	  // Comprobamos los límites
		  if( indice >= 0 && indice <= 4 )
		  {
			jugadores[indice] = value;
	  	  }
	  }
	}

	// Método principal
	public static void Main ()
	{
		// Vamos a crear un gran equipo
		Equipo osasuna = new Equipo("C.A. Osasuna");

		// Y ahora ya podemos establecer los nombre
		// de jugadores de forma directa
		osasuna[0] = "Ricardo";
		osasuna[1] = "Krutxi";
		osasuna[2] = "Josetxo";
		osasuna[3] = "Iñaki Muñoz";
		osasuna[4] = "Raúl García";

		// Y ahora ya podemos recorrer el array
		for(int i = 0; i < osasuna.getSize(); i++)
		{
			System.Console.WriteLine("Jugador {0}", osasuna[i]);
		}
	}

}
Por otro los métodos delegados, similares a los de las interfaces
/**
* Delegados.cs
* Los métodos delegados son similares a los interfaces: definen
* una forma de método (con su nombre, su return y sus parámetros)
* pero sin implementación.
*/

using System;

// Definimos la clase Aritmetica
// que sirve para hacer operaciones simples
public class Aritmetica
{

	// Aquí definimos el método delegado
	// Que deberán implementar otras clases
	public delegate int Operacion(int a, int b);

	// Fijate en el parámetro
	public int HacerOperacion (Operacion op, int x, int y)
	{
		return (op(x, y));
	}
}

public class UsarAritmetica
{
	// Un método que implementa Operación
	public static int OperacionSumar(int a, int b)
	{
		return (a+b);
	}

	// Un método que implementa Operación
	public static int OperacionRestar(int a, int b)
	{
		return (a-b);
	}

	// Método principal
	public static void Main()
	{
		// ATENCION. Se crea una instancia del MÉTODO DELEGADO
		Aritmetica.Operacion sumar = new Aritmetica.Operacion(OperacionSumar);
		Aritmetica.Operacion restar = new Aritmetica.Operacion(OperacionRestar);

		// Y ahora creamos una instancia de la clase que contiene el delegado
		Aritmetica aritmetica = new Aritmetica();

		// Y ahora usamos los métodos delegados
		int resultado = aritmetica.HacerOperacion(sumar, 665, 1);

		System.Console.WriteLine("Que fuerte, ha salido {0}", resultado);

		System.Console.WriteLine("Y ahora {0}",  aritmetica.HacerOperacion(restar, 700, 34));

	}

}
Vamos a ver, como implementar nuestros propios eventos. Es algo complejo, y hay que ir paso a paso
/**
* Eventos.cs
* Programas que muestran la definición y uso de Eventos.
* Los eventos som situaciones en la ejecución de un programa
* que pueden ser controlados para cambiar el comportamiento del mismo.
* El ejemplo más claro es el entorno de ventanas: el evento de pinchar en un botón
* puede provocar el cierre de una ventana.
*/

using System;

// En esta caso vamos a crear un evento para controlar que el usuario
// ha escrito la palabra viagra en la consola. El evento se va a disparar
// cuando se asigne lo que se ha metido por pantalla a una variable: en ese
// momento se comprobará si esa palabra es "viagra" y de ser así, avisará

// Definimos el método delegado
// Los parametros son:
// 1. un objeto: el que genera el evento
// 2. argumento del evento: una clase especifica para ese evento
public delegate void capturaPalabraSpam(Object objeto, ArgumentoEvento e);

// Definimos una clase para los argumentos del evento
// que DEBE heredar de la clase: EventArgs
public class ArgumentoEvento : EventArgs
{
	public string palabra;

	// Constructor
	public ArgumentoEvento(string palabra)
	{
		this.palabra = palabra;
	}
}

// Ahora una clase para comprobar el evento
public class CompruebaSpam
{
	string palabraspam;
	public event capturaPalabraSpam EventoSpam;

	// Atencion cuando hagamos el set (la asignación) se disparará el evento!
	public string PalabraSpam
	{
		get { return palabraspam; }
		set
		{
			if ( EventoSpam != null)
			{
				ArgumentoEvento argumentos = new ArgumentoEvento(value);
				EventoSpam(this, argumentos);
				palabraspam = argumentos.palabra;
			}
		}
	}
}

public class PruebaEvento
{
	// Handler: IMPLEMENTACION DEL MÉTODO DELEGADO
	// Definimos el handler para el evento
	static void detectaSpam(Object source, ArgumentoEvento e )
	{
		if (e.palabra.ToLower() == "viagra")
		{
			System.Console.WriteLine("SPAM!!");
			System.Console.WriteLine("Detectada palabra basura {0}",e.palabra);
		}
	}

	// Método principal
	public static void Main ()
	{

		// creamos una instancia del comprobador
		CompruebaSpam comprueba = new CompruebaSpam();

		// Le asignamos el handler de eventos que hemos implementado
		comprueba.EventoSpam += new capturaPalabraSpam(detectaSpam);

		System.Console.WriteLine("Vamos a ver si generamos un evento");
		System.Console.WriteLine("Escribe algo, distinto de viagra, o no.");

		comprueba.PalabraSpam = System.Console.ReadLine();

	}

}