Este sitio web usa cookies de terceros para analizar el tráfico y personalizar los anuncios. Si no está de acuerdo, abandone el sitio y no siga navegando por él. ×


7.5. La palabra this: el objeto actual

Podemos hacer referencia al objeto que estamos usando, con "this". Un primer uso es dar valor a los atributos, incluso si los parámetros tienen el mismo nombre que éstos:

public void MoverA (int x, int y)
{
    this.x = x;
    this.y = y;
}

Un fuente completo sería así:

// Ejemplo_07_05a.cs
// Primer ejemplo de uso de "this": parámetros
// Introducción a C#, por Nacho Cabanes

using System;

public class Ejemplo_07_05a
{
    public static void Main()
    {
        Titulo t = new Titulo(38,5,"Hola");
        t.Escribir();
    }
}

// ------------------

public class Titulo
{
    private int x;
    private int y;
    private string texto;
    
    public Titulo(int x, int y, string texto)
    {
        this.x = x;
        this.y = y;
        this.texto = texto;
    }
    
    public void Escribir()
    {
        Console.SetCursorPosition(x,y);
        Console.WriteLine(texto);
    }
}

En muchos casos, podemos evitar este uso de "this". Por ejemplo, normalmente es preferible usar otro nombre en los parámetros:

public void MoverA (int nuevaX, int nuevaY)
{
    this.x = nuevaX;
    this.y = nuevaY;
}

Y en ese caso se puede (y se suele) omitir "this":

public void MoverA (int nuevaX, int nuevaY)
{
    x = nuevaX;
    y = nuevaY;
}


Pero "this" tiene también otros usos. Por ejemplo, podemos crear un constructor a partir de otro que tenga distintos parámetros:

public void MoverA (int nuevaX, int nuevaY)
{
    x = nuevaX;
    y = nuevaY;
}

En ese ejemplo, existiría un constructor de RectanguloRelleno que recibiera sólo la coordenada X (y que podría prefijar la Y al valor 0, por ejemplo), y también existe este otro constructor, que recibe X e Y, y que se basa en el anterior para fijar el valor de X.

Nuevamente, podríamos hacer un fuente completo de ejemplo:

// Ejemplo_07_05b.cs
// Segundo ejemplo de uso de "this": constructores
// Introducción a C#, por Nacho Cabanes

using System;

public class Ejemplo_07_05b
{
    public static void Main()
    {
        Titulo t = new Titulo(38,5,"Hola");
        t.Escribir();
    }
}

// ------------------

public class Titulo
{
    private int x;
    private int y;
    private string texto;
    
    public Titulo(int nuevaX, int nuevaY, string txt)
    {
        x = nuevaX;
        y = nuevaY;
        texto = txt;
    }
    
    public Titulo(int nuevaY, string txt)
        : this (40-txt.Length/2, nuevaY, txt)
    {
    }
    
    public void Escribir()
    {
        Console.SetCursorPosition(x,y);
        Console.WriteLine(texto);
    }
}


La palabra "this" se usa mucho también para que unos objetos "conozcan" a los otros. Por ejemplo, en un juego de 2 jugadores, podríamos tener una clase Jugador, heredar de ella las clases Jugador1 y Jugador2, que serán muy parecidas entre sí, y nos podríamos sentir tentados de hacer que la clase Jugador tenga un "adversario" como atributo, y que el Jugador1 indique que su adversario es de tipo Jugador2, y lo contrario para el otro jugador, así:

// Ejemplo_07_05c.cs
// Dos clases que se usan mutuamente: recursividad indirecta
// Introducción a C#, por Nacho Cabanes

using System;

public class Ejemplo_07_05c
{
    public static void Main()
    {
        Jugador1 j1 = new Jugador1();
        Jugador2 j2 = new Jugador2();
    } 
}

// ------------------

public class Jugador
{
    protected Jugador adversario;
}

// ------------------

public class Jugador1 : Jugador
{
    public Jugador1() 
    {
        adversario = new Jugador2();
    }
}
  
// ------------------

public class Jugador2 : Jugador
{
    public Jugador2() 
    {
        adversario = new Jugador1();
    }
}

Pero este programa se queda colgado un instante, hasta terminar finalmente lanzando una excepción de desbordamiento de pila (Stack Overflow), porque estamos creando una recursividad indirecta sin fin: el jugador1 crea un jugador2, éste crea un nuevo jugador1 (en vez de utilizar el ya existente), que a su vez crea un nuevo jugador2 y así sucesivamente, hasta finalmente desbordar la zona de memoria que está reservada para llamadas recursivas.

Una alternativa mucho menos peligrosa (pero que, a cambio, complica el programa principal), es que sea el programa principal el que indique a cada jugador quién es su adversario:

// Ejemplo_07_05d.cs
// Dos clases que se usan mutuamente: "Main" coordina
// Introducción a C#, por Nacho Cabanes

using System;

public class Ejemplo_07_05d
{
    public static void Main()
    {
        Jugador j1 = new Jugador();
        Jugador j2 = new Jugador();
        j1.SetAdversario(j2);
        j2.SetAdversario(j1);
    } 
}

// ------------------

public class Jugador
{
    protected Jugador adversario;
    
    public void SetAdversario(Jugador nuevoAdversario)
    {
        adversario = nuevoAdversario;
    }
}

Otra alternativa es que un Jugador le pueda decir a otro "yo soy tu adversario", y ese "yo" equivaldrá a un "this". En general, eso simplificará el programa principal, a cambio de complicar ligeramente las clases auxiliares:

// Ejemplo_07_05e.cs
// Dos clases que se usan mutuamente: "this"
// Introducción a C#, por Nacho Cabanes

using System;

public class Ejemplo_07_05e
{
    public static void Main()
    {
        Jugador j1 = new Jugador();   // Creado sin más detalles
        Jugador j2 = new Jugador(j1); // Indicando su adversario
    } 
}

// ------------------

public class Jugador
{
    protected Jugador adversario;
    
    public Jugador() 
    {
    }
    
    public Jugador(Jugador adversario)
    {
        // Mi adversario es el que me indican
        SetAdversario( adversario );  
        
        // Y yo soy su adversario
        adversario.SetAdversario( this );
    }
    
    public void SetAdversario(Jugador nuevoAdversario)
    {
        adversario = nuevoAdversario;
    }
}

Hay que recordar que, en general, cuando una clase contiene a otras, la clase contenedora sabe los detalles de las clases contenidas (la "casa" conoce a sus "puertas"), pero las clases contenidas no saben nada de la clase que las contiene (las "puertas" no saben otros detalles de la "casa" a la que pertenecen). Si queremos que se puedan comunicar con la clase contenedora, deberemos usar "this" para que la conozcan, en vez de crear una nueva clase contenedora con "new", o provocaremos una recursividad indirecta sin fin, como en el primer ejemplo.

Ejercicios propuestos:

Ejercicio propuesto 7.5.1: Crea una versión ampliada del ejercicio 7.4.2, en la que el constructor sin parámetros de la clase "Trabajador" se apoye en otro constructor que reciba como parámetro el nombre de esa persona. La versión sin parámetros asignará el valor "Nombre no detallado" al nombre de esa persona.
Ejercicio propuesto 7.5.2: Crea una clase Puerta con un ancho, un alto y un método "MostrarEstado" que muestre su ancho y su alto. Crea una clase Casa, que contenga 3 puertas y otro método "MostrarEstado" que escriba "Casa" y luego muestre el estado de sus tres puertas.
Ejercicio propuesto 7.5.3: Crea una clase Casa, con una superficie (por ejemplo, 90 m2) y un método "MostrarEstado" que escriba su superficie. Cada casa debe contener 3 puertas. Las puertas tendrán un ancho, un alto y un método "MostrarEstado" que muestre su ancho y su alto y la superficie de la casa en la que se encuentran. Crea un programa de prueba que cree una casa y muestre sus datos y los de sus tres puertas.
Ejercicio propuesto 7.5.4: Amplía el esqueleto de ConsoleInvaders (7.4.4), de modo que el constructor sin parámetros de la clase Nave se apoye en el constructor con parámetros de la misma clase.