Utilizar propiedades (Guía de programación de C#)

Las propiedades combinan aspectos de los campos y de los métodos. Para el usuario de un objeto, una propiedad es similar a un campo, el acceso a la propiedad requiere la misma sintaxis. Para el implementador de una clase, una propiedad es uno o dos bloques de código que representan un descriptor de acceso get y un descriptor de acceso set. El bloque de código para el descriptor de acceso get se ejecuta cuando se lee la propiedad; el bloque de código para el descriptor de acceso set se ejecuta cuando se asigna un nuevo valor a la propiedad. Una propiedad sin un descriptor de acceso set se considera de sólo lectura. Una propiedad sin un descriptor de acceso get se considera de sólo escritura. Una propiedad con ambos descriptores de acceso es de lectura y escritura.

A diferencia de los campos, las propiedades no están clasificadas como variables. Por lo tanto, no se puede pasar una propiedad como un parámetro ref (Referencia de C#) o out (Referencia de C#).

Las propiedades tienen muchos usos: validan datos antes de permitir un cambio; exponen datos de forma transparente en una clase donde se recuperan realmente los datos de otro origen, como una base de datos; realizan una acción cuando se modifican datos, por ejemplo, provocar un evento, o cambian el valor de otros campos.

Las propiedades se declaran en el bloque de clase especificando el nivel de acceso del campo, seguido por el tipo de la propiedad, por el nombre de la propiedad y por un bloque de código que declara un descriptor de acceso get y/o un descriptor de acceso set. Por ejemplo:

public class Date
{
    private int month = 7;  // Backing store

    public int Month
    {
        get
        {
            return month;
        }
        set
        {
            if ((value > 0) && (value < 13))
            {
                month = value;
            }
        }
    }
}

En este ejemplo, Month se declara como una propiedad para que el descriptor de acceso set pueda asegurarse de que Month se establezca en un valor comprendido entre 1 y 12. La propiedad Month utiliza un campo privado para realizar el seguimiento del valor real. La ubicación real de los datos de una propiedad suele conocerse como "almacén de respaldo" de la propiedad. Es normal que las propiedades utilicen campos privados como almacén de respaldo. El campo se marca como privado para asegurarse de que sólo puede cambiarse llamando a la propiedad. Para obtener más información sobre restricciones de acceso público y privado, vea Modificadores de acceso (Guía de programación de C#).

Las propiedades autoimplementadas ofrecen una sintaxis simplificada para las declaraciones de propiedades simples. Para obtener más información, vea Propiedades autoimplementadas (Guía de programación de C#).

El descriptor de acceso get

El cuerpo del descriptor de acceso get se parece al de un método. Debe devolver el valor del tipo de la propiedad. La ejecución del descriptor de acceso get equivale a leer el valor del campo. Por ejemplo, cuando se devuelve la variable privada del descriptor de acceso get y las optimizaciones están habilitadas, el compilador pone la llamada al método del descriptor de acceso get entre líneas para que no se produzca una sobrecarga de método-llamada. Sin embargo, un método de descriptor de acceso get virtual no se puede poner entre líneas, porque el compilador no sabe en tiempo de compilación qué método puede llamarse en tiempo de ejecución. A continuación, se muestra un descriptor de acceso get que devuelve el valor de un campo privado name:

class Person
{
    private string name;  // the name field
    public string Name    // the Name property
    {
        get
        {
            return name;
        }
    }
}

Cuando se haga referencia a la propiedad, se llamará al descriptor de acceso get para leer el valor de la misma, salvo en el caso de que la referencia se haga como el destino de una asignación. Por ejemplo:

Person person = new Person();
//...

System.Console.Write(person.Name);  // the get accessor is invoked here

El descriptor de acceso get debe terminar en una instrucción return o throw, y el control no puede salir del cuerpo del descriptor de acceso.

Cambiar el estado de un objeto mediante el descriptor de acceso get es una técnica de programación poco recomendable. Por ejemplo, el siguiente descriptor de acceso cambia, como efecto secundario, el estado del objeto cada vez que se obtiene acceso al campo number.

private int number;
public int Number
{
    get
    {
        return number++;   // Don't do this
    }
}

Se puede utilizar el descriptor de acceso get para devolver el valor de un campo o para calcularlo y devolverlo. Por ejemplo:

class Employee
{
    private string name;
    public string Name
    {
        get
        {
            return name != null ? name : "NA";
        }
    }
}

El segmento de código anterior devolverá el valor NA si no se asigna ningún valor a la propiedad Name.

El descriptor de acceso set

El descriptor de acceso set es similar a un método cuyo tipo de valor devuelto es void. Utiliza un parámetro implícito denominado value, que tiene el mismo tipo que la propiedad. En el siguiente ejemplo se agrega un descriptor de acceso set a la propiedad Name:

class Person
{
    private string name;  // the name field
    public string Name    // the Name property
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
        }
    }
}

Cuando se asigne un valor a la propiedad, se llamará al descriptor de acceso set utilizando un argumento que proporcione el nuevo valor. Por ejemplo:

Person person = new Person();
person.Name = "Joe";  // the set accessor is invoked here                

System.Console.Write(person.Name);  // the get accessor is invoked here

No es correcto utilizar el nombre de parámetro implícito, value, para una declaración de variable local en un descriptor de acceso set.

Comentarios

Las propiedades pueden marcarse como public, private, protected, internal o protected internal. Estos modificadores de acceso definen cómo pueden tener acceso a las propiedades los usuarios de la clase. Los descriptores de acceso get y set para la misma propiedad pueden tener modificadores de acceso diferentes. Por ejemplo, el descriptor de acceso get puede ser public para permitir el acceso de sólo lectura desde fuera del tipo, y el descriptor de acceso set puede ser private o protected. Para obtener más información, vea Modificadores de acceso (Guía de programación de C#).

Una propiedad puede declararse como propiedad estática mediante la palabra clave static. Esto hace que la propiedad esté siempre disponible para los llamadores, aunque no exista ninguna instancia de la clase. Para obtener más información, vea Clases estáticas y sus miembros (Guía de programación de C#).

Una propiedad puede marcarse como propiedad virtual mediante la palabra clave virtual. Esto permite que las clases derivadas invaliden el comportamiento de la propiedad mediante la palabra clave override. Para obtener más información acerca de estas opciones, vea Herencia (Guía de programación de C#).

Una propiedad que reemplaza una propiedad virtual también puede ser de tipo sealed, lo cual especifica que ya no es virtual para las clases derivadas. Por último, una propiedad puede declararse como abstract. Esto significa que no hay ninguna implementación en la clase, y las clases derivadas deben escribir su propia implementación. Para obtener más información acerca de estas opciones, vea Clases y miembros de clase abstractos y sellados (Guía de programación de C#).

Nota

No es correcto utilizar un modificador virtual (Referencia de C#), abstract (Referencia de C#) o override (Referencia de C#) en un descriptor de acceso de una propiedad static.

Ejemplo

En este ejemplo se muestra el uso de propiedades de instancia, estáticas y de sólo lectura. Lee el nombre del empleado escrito mediante el teclado, incrementa el número de empleados NumberOfEmployees en una unidad y muestra el nombre del empleado y su número correspondiente.

public class Employee
{
    public static int NumberOfEmployees;
    private static int counter;
    private string name;

    // A read-write instance property:
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    // A read-only static property:
    public static int Counter
    {
        get { return counter; }
    }

    // A Constructor:
    public Employee()
    {
        // Calculate the employee's number:
        counter = ++counter + NumberOfEmployees;
    }
}

class TestEmployee
{
    static void Main()
    {
        Employee.NumberOfEmployees = 107;
        Employee e1 = new Employee();
        e1.Name = "Claude Vige";

        System.Console.WriteLine("Employee number: {0}", Employee.Counter);
        System.Console.WriteLine("Employee name: {0}", e1.Name);
    }
}
/* Output:
    Employee number: 108
    Employee name: Claude Vige
*/

En este ejemplo se muestra el método para tener acceso a una propiedad de una clase base que está oculta por otra propiedad con el mismo nombre de una clase derivada.

public class Employee
{
    private string name;
    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}

public class Manager : Employee
{
    private string name;

    // Notice the use of the new modifier:
    public new string Name
    {
        get { return name; }
        set { name = value + ", Manager"; }
    }
}

class TestHiding
{
    static void Main()
    {
        Manager m1 = new Manager();

        // Derived class property.
        m1.Name = "John";

        // Base class property.
        ((Employee)m1).Name = "Mary";

        System.Console.WriteLine("Name in the derived class is: {0}", m1.Name);
        System.Console.WriteLine("Name in the base class is: {0}", ((Employee)m1).Name);
    }
}
/* Output:
    Name in the derived class is: John, Manager
    Name in the base class is: Mary
*/

Es importante tener en cuenta los siguientes detalles del ejemplo anterior:

  • La propiedad Name de la clase derivada oculta la propiedad Name de la clase base. En este caso, se utiliza el modificador new en la declaración de la propiedad de la clase derivada:

    public new string Name
    
  • Se utiliza la conversión (Employee) para tener acceso a la propiedad oculta de la clase base:

    ((Employee)m1).Name = "Mary";
    

    Para obtener más información acerca de los miembros ocultos, consulte new (Modificador, Referencia de C#).

En este ejemplo se utilizan dos clases, Cube y Square para implementar una clase abstracta, Shape, y reemplazar su propiedad abstracta Area. Conviene resaltar que se utiliza el modificador override en las propiedades. El programa lee el valor del lado como entrada y calcula las superficies del cuadrado y el cubo. También lee el valor de la superficie como entrada y calcula el lado correspondiente del cuadrado y el cubo.

abstract class Shape
{
    public abstract double Area
    {
        get;
        set;
    }
}

class Square : Shape
{
    public double side;

    public Square(double s)  //constructor
    {
        side = s;
    }

    public override double Area
    {
        get
        {
            return side * side;
        }
        set
        {
            side = System.Math.Sqrt(value);
        }
    }
}

class Cube : Shape
{
    public double side;

    public Cube(double s)
    {
        side = s;
    }

    public override double Area
    {
        get
        {
            return 6 * side * side;
        }
        set
        {
            side = System.Math.Sqrt(value / 6);
        }
    }
}

class TestShapes
{
    static void Main()
    {
        // Input the side:
        System.Console.Write("Enter the side: ");
        double side = double.Parse(System.Console.ReadLine());

        // Compute the areas:
        Square s = new Square(side);
        Cube c = new Cube(side);

        // Display the results:
        System.Console.WriteLine("Area of the square = {0:F2}", s.Area);
        System.Console.WriteLine("Area of the cube = {0:F2}", c.Area);
        System.Console.WriteLine();

        // Input the area:
        System.Console.Write("Enter the area: ");
        double area = double.Parse(System.Console.ReadLine());

        // Compute the sides:
        s.Area = area;
        c.Area = area;

        // Display the results:
        System.Console.WriteLine("Side of the square = {0:F2}", s.side);
        System.Console.WriteLine("Side of the cube = {0:F2}", c.side);
    }
}
/* Example Output:
    Enter the side: 4
    Area of the square = 16.00
    Area of the cube = 96.00

    Enter the area: 24
    Side of the square = 4.90
    Side of the cube = 2.00
*/

Vea también

Referencia

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

Propiedades de interfaces (Guía de programación de C#)

Propiedades autoimplementadas (Guía de programación de C#)

Conceptos

Guía de programación de C#