Curso de C# : Campos y Propiedades
Campos
Ambas
cosas (campos y propiedades) representan a los datos de una clase,
aunque cada uno de ellos lo hace de un modo diferente. Los campos de una
clase se construyen a base de variables.
class Circunferencia
{
public double Radio;
public double Perimetro;
public double Area;
public const double PI=3.1415926;
}
Los
modificadores de acceso de los que se habló al comienzo de este curso
(private, protected, internal y public) son aplicables a las variables y
constantes solamente cuando estas representan los campos de una clase, y
para ello deben estar declaradas como miembros de la misma dentro de su
bloque. Sin embargo, una variable que esté declarada en otro bloque
distinto (dentro de un método, por ejemplo) no podrá ser un campo de la
clase, pues será siempre privada para el código que esté dentro de ese
bloque, de modo que no se podrá acceder a ella desde fuera del mismo.
Al
instanciar el objeto, se ejecutará su constructor dando los valores
adecuados a los campos Area y Perimetro. Sin embargo, después el cliente
puede modificar los valores de estos campos, asignándole valores a su
antojo y haciendo, por lo tanto, que dichos valores no sean coherentes
(claro, si el radio vale 4, el perímetro no puede ser 1, ni el área
puede ser 2). Para evitar esta falta de seguridad usamos algo que no
existía hasta ahora en ningún otro lenguaje: los campos de sólo lectura
(ojo, campos, no propiedades).
class Circunferencia
{
public double Radio;
public readonly double Perimetro;
public readonly double Area;
public const double PI=3.1415926;
}
Ahora
están protegidos los campos Perimetro y Area, pues son de sólo lectura,
de modo que ahora el cliente no podrá modificar los valores de dichos
campos. Se ha puesto la palabra readonly delante del tipo del campo. Sin
embargo, sigue habiendo un problema: si, después de instanciar la
clase, el cliente puede modificar el valor del radio. El radio volvería a
no ser coherente con el resto de los datos del objeto. Podría ponerse
también el campo Radio como de sólo lectura, pero tendríamos que
instanciar un nuevo objeto cada vez que necesitásemos un radio distinto.
Podríamos poner el radio también como campo de sólo lectura y escribir
un método para que el cliente pueda modificar el radio, y escribir en
él, código para modificar los tres campos, de modo que vuelvan a ser
coherentes. Esto no se puede hacer porque los campos readonly solamente
pueden ser asignados una vez en el constructor, y a partir de aquí su
valor es constante y no se puede variar en esa instancia.
No
utilizar constantes en vez de campos de sólo lectura. Pues para poder
usar constantes hay que conocer previamente el valor que van a tener
(como la constante PI, que siempre vale lo mismo), pero, en este caso,
conocemos el los valores hasta que no se ejecute el programa. Los campos
de sólo lectura almacenan valores constantes que no se conocerán hasta
que el programa esté en ejecución.
Los
campos, igual que los métodos y los constructores, también pueden ser
static. Su comportamiento sería parecido: un campo static tiene mucho
más que ver con la clase que con una instancia particular de ella. Si
deseamos añadir una descripción a la clase circunferencia, podemos usar
un campo static, porque todas las instancias de esta clase se ajustarán
necesariamente a dicha descripción. Si ponemos el modificador static a
un campo de sólo lectura, este campo ha de ser inicializado en un
constructor static. Pero las constantes no aceptan el modificador de
acceso static: si su modificador de acceso es public o internal ya se
comportará como su fuera un campo static. Ejemplo:
class Circunferencia
{
static Circunferencia()
{
Descripcion="Polígono regular de infinitos lados";
}
public Circunferencia(double radio)
{
this.Radio=radio;
this.Perimetro=2 * PI * this.Radio;
this.Area=PI * Math.Pow(this.Radio,2);
}
public double Radio;
public readonly double Perimetro;
public readonly double Area;
public const double PI=3.1415926;
public static readonly string Descripcion;
}
La
clase Circunferencia tiene un constructor static que inicializa el
valor del campo Descripción, que también es static. El objetivo es que
esta clase contenga siempre datos coherentes, dado que el área y el
perímetro siempre estarán en función del radio, y que el radio se pueda
modificar sin necesidad de volver a instanciar la clase. No es posible
utilizar campos ni campos de sólo lectura, pues los primeros no permiten
controlar los datos que contienen, y los segundos no permiten modificar
su valor después de ser inicializados en el constructor.
Es
posible cambiar los modificadores de acceso de los campos, haciéndolos
private o protected en lugar de public, y después escribir métodos para
retornar sus valores. Sin embargo, se trata de un modo muy poco
intuitivo, y poco natural.
Propiedades
Las
propiedades también representan los datos de los objetos de una clase,
pero lo hacen de un modo distinto a los campos. Los campos no nos
permiten tener el control de su valor salvo que sean de sólo lectura, y
si son de sólo lectura solamente se podían asignar una vez en el
constructor. Esto puede ser útil en ocasiones, pero no siempre. Las
propiedades solventan todos estos problemas: por un lado nos permiten
tener un control absoluto de los valores que reciben o devuelven, y
además no existen limitaciones para modificar y cambiar sus valores
tantas veces como sea preciso.
Las
propiedades funcionan internamente como si fueran métodos, esto es,
ejecutan el código que se encuentra dentro de su bloque, pero se
muestran al cliente como si fueran campos, es decir, datos. La sintaxis
de una propiedad es la siguiente:
acceso [static] tipo NombrePropiedad
{
get
{
// Código para calcular el valor de retorno (si procede)
return ValorRetorno;
}
set
{
// Código para validar y/o asignar el valor de la propiedad
}
}
El
modificador de acceso, puede ser cualquiera de los que se usan también
para los campos. Si no se indica, será private. Después la palabra
static si se desea definir como propiedad estática, sería accesible sin
instanciar objetos de la clase, pero no accesible desde las instancias
de la misma (como los campos static). Posteriormente el tipo del dato
que almacenará la propiedad (cualquier tipo valor o cualquier tipo
referencia), seguido del nombre de la propiedad. Dentro del bloque de la
propiedad hay otros dos bloques: el bloque get es el bloque de retorno,
el que nos permitirá ver lo que vale la propiedad desde la aplicación
cliente; y el bloque set es el bloque de asignación de la propiedad, el
que nos permitirá asignarle valores desde la aplicación cliente. El
orden en que se pongan los bloques get y set es indiferente. S si se
omite el bloque de asignación (set) habremos construido una propiedad de
sólo lectura. Y viceversa. Ejemplo:
class Circunferencia
{
public Circunferencia(double radio)
{
this.radio=radio;
}
private double radio;
const double PI=3.1415926;
public double Radio
{
get
{
return this.radio;
}
set
{
this.radio=value;
}
}
public double Perimetro
{
get
{
return 2 * PI * this.radio;
}
}
public double Area
{
get
{
return PI * Math.Pow(this.radio, 2);
}
}
}
No
se han escrito métodos para modificar el radio ni para obtener los
valores de las otros datos, sino que se han escrito propiedades. Con
esto se consigue que el cliente pueda acceder a los datos de un modo
mucho más natural. A continuación se muestra un ejemplo de método Main:
static void Main()
{
Circunferencia c=new Circunferencia(4);
Console.WriteLine("El radio de la circunferencia es {0}",c.Radio);
Console.WriteLine("El perímetro de la circunferencia es {0}",
c.Perimetro);
Console.WriteLine("El área de la circunferencia es {0}", c.Area);
Console.WriteLine("Pulsa INTRO para incrementar el Radio en 1");
string a = Console.ReadLine();
c.Radio++;
Console.WriteLine("El radio de la circunferencia es {0}",c.Radio);
Console.WriteLine("El perímetro de la circunferencia es {0}",
c.Perimetro);
Console.WriteLine("El área de la circunferencia es {0}", c.Area);
a=Console.ReadLine();
}
Se
accede a las propiedades tal y como se accedería a los datos de la
clase a definida con campos. Sin embargo se obtiene un control absoluto
sobre los datos de la clase gracias a las propiedades. Es posible
modificar el valor del Radio con toda naturalidad (en la línea
c.Radio++) y esta modificación afecta también a las propiedades
Perimetro y Area. Cuando se instancia el objeto, se ejecuta su
constructor, asignándole el valor que se pasa como argumento al campo
radio (que es protected y, por lo tanto, no accesible desde el cliente).
Cuando se recupera el valor de la propiedad Radio para escribirlo en la
consola se ejecuta el bloque "get" de dicha propiedad, y este bloque
devuelve, el valor del campo radio, que era la variable donde se
almacenaba este dato. Cuando se recuperan los valores de las otras dos
propiedades también para escribirlos en la consola sucede lo mismo, se
ejecutan los bloques get de cada una de ellas que, retornan el resultado
de calcular dichos datos. Por último, cuando se incrementa el valor del
radio (c.Radio++) lo que se ejecuta es el bloque set de la propiedad,
se asigna el nuevo valor (representado por "value") a la variable
protected radio.
Las
propiedades Area y Perimetro no tienen bloque set, son propiedades de
sólo lectura. La diferencia de una propiedad con un campos de sólo
lectura es que un campo de sólo lectura ha de estar representado
necesariamente por una variable, y, además, solamente se le puede
asignar el valor una vez en el constructor; por contra, el que una
propiedad sea de sólo lectura no implica que su valor sea constante,
sino única y exclusivamente que no puede ser modificado por el cliente.
Si se hubiéran puesto campos de sólo lectura no sería posible
modificarlos ni por el cliente, ni por la propia clase. Value es una
variable que declara y asigna implícitamente el compilador en un bloque
set para que sepamos cuál es el valor que el cliente quiere asignar a
la propiedad, si se escribe c.Radio=8, value valdría 8. De este modo
podremos comprobar si el valor que se intenta asignar a la propiedad es
adecuado.
Las
propiedades funcionan internamente como si fueran métodos, pero no es
necesario poner los paréntesis cuando son invocadas, pues se accede a
ellas como si fueran campos. Sin embargo esto no quiere decir que
siempre sea mejor escribir propiedades en lugar de métodos. Las
propiedades se utilizan para hacer un uso más natural de los objetos.
Hay que escribir un método cuando este implique una acción, y una
propiedad cuando esta implique un dato.
Comentarios
Publicar un comentario