clases. - upv/ehuehu.eus/mrodriguez/archivos/csharppdf/lenguaje/clases.pdf · objeto en...

35
Marco Besteiro y Miguel Rodríguez Clases 1/35 Clases. Introducción La Programación Orientada a Objetos permite realizar grandes programas mediante la unión de elementos más simples, que pueden ser diseñados y comprobados de manera independiente del programa que va a usarlos. Muchos de estos elementos podrán ser reutilizados en otros programas. A estas “piezas”, “módulos” o "componentes", que interactúan entre sí cuando se ejecuta un programa, se les denomina objetos. Los objetos contienen datos y funciones que actúan sobre esos datos. Durante la ejecución de un programa, los objetos interactúan entre sí pasándose mensajes y devolviendo respuestas a estos mensajes. Es fundamental darse cuenta de que un objeto no necesita conocer el funcionamiento interno de los demás objetos para poder interactuar con ellos (igual que el hombre no necesita conocer cómo funciona el motor de su coche, el televisor o un ordenador para poder utilizarlos), sino que le es suficiente con saber la forma en que debe enviarle sus mensajes y cómo va a recibir la respuesta (al hombre le puede ser suficiente para utilizar el coche saber cómo conducir – acelerar, utilizar el volante, etc...-, cómo funcionan el interruptor, el dial del volumen y los botones de cambio de canal para utilizar un televisor, etc). Sucede a menudo que hay que utilizar varios ejemplares análogos de un determinado tipo (por ejemplo varias ventanas en la pantalla del PC, varios usuarios, varios clientes, varias cuentas corrientes de un banco, etc.). La definición genérica de estos objetos análogos se realiza mediante la clase. Así, una clase contiene una completa y detallada descripción de la información y de las funciones que contendrá cada objeto de esa clase. Los objetos son las variables concretas que se crean de una determinada clase. A veces se llaman también instancias. Así, se habla de crear un objeto o instancia de una clase. Las clases son verdaderos tipos de datos definidos por el usuario o por el sistema y pueden ser utilizados de igual manera que los tipos de datos simples, tales como int, float, etc. Un objeto tiene su propio conjunto de datos o variables miembro, aunque no de funciones, ya que las que se aplican a un objeto concreto son propias de la clase a la que pertenece el objeto. Para proteger a las variables de modificaciones no deseadas se introduce el concepto de encapsulación, ocultamiento o abstracción de datos. De ordinario una clase ofrece un conjunto de métodos públicos a través de los cuales se puede actuar sobre los datos, que serán privados. Estas funciones o métodos públicos constituyen la interfaz de la clase, es decir, el elemento que el programador puede utilizar para comunicarse con los objetos. . De esta forma se garantiza que se hace buen uso de los objetos, manteniendo la coherencia de la información. Esto sería imposible si se accediera libre e independientemente a cada variable miembro. Al usuario le es suficiente con saber

Upload: buibao

Post on 11-Nov-2018

216 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

1/35

Clases.

Introducción La Programación Orientada a Objetos permite realizar grandes programas mediante la unión de elementos más simples, que pueden ser diseñados y comprobados de manera independiente del programa que va a usarlos. Muchos de estos elementos podrán ser reutilizados en otros programas. A estas “piezas”, “módulos” o "componentes", que interactúan entre sí cuando se ejecuta un programa, se les denomina objetos. Los objetos contienen datos y funciones que actúan sobre esos datos. Durante la ejecución de un programa, los objetos interactúan entre sí pasándose mensajes y devolviendo respuestas a estos mensajes. Es fundamental darse cuenta de que un objeto no necesita conocer el funcionamiento interno de los demás objetos para poder interactuar con ellos (igual que el hombre no necesita conocer cómo funciona el motor de su coche, el televisor o un ordenador para poder utilizarlos), sino que le es suficiente con saber la forma en que debe enviarle sus mensajes y cómo va a recibir la respuesta (al hombre le puede ser suficiente para utilizar el coche saber cómo conducir –acelerar, utilizar el volante, etc...-, cómo funcionan el interruptor, el dial del volumen y los botones de cambio de canal para utilizar un televisor, etc). Sucede a menudo que hay que utilizar varios ejemplares análogos de un determinado tipo (por ejemplo varias ventanas en la pantalla del PC, varios usuarios, varios clientes, varias cuentas corrientes de un banco, etc.). La definición genérica de estos objetos análogos se realiza mediante la clase. Así, una clase contiene una completa y detallada descripción de la información y de las funciones que contendrá cada objeto de esa clase. Los objetos son las variables concretas que se crean de una determinada clase. A veces se llaman también instancias. Así, se habla de crear un objeto o instancia de una clase. Las clases son verdaderos tipos de datos definidos por el usuario o por el sistema y pueden ser utilizados de igual manera que los tipos de datos simples, tales como int, float, etc. Un objeto tiene su propio conjunto de datos o variables miembro, aunque no de funciones, ya que las que se aplican a un objeto concreto son propias de la clase a la que pertenece el objeto. Para proteger a las variables de modificaciones no deseadas se introduce el concepto de encapsulación, ocultamiento o abstracción de datos. De ordinario una clase ofrece un conjunto de métodos públicos a través de los cuales se puede actuar sobre los datos, que serán privados. Estas funciones o métodos públicos constituyen la interfaz de la clase, es decir, el elemento que el programador puede utilizar para comunicarse con los objetos. . De esta forma se garantiza que se hace buen uso de los objetos, manteniendo la coherencia de la información. Esto sería imposible si se accediera libre e independientemente a cada variable miembro. Al usuario le es suficiente con saber

Page 2: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

2/35

cómo comunicarse con un objeto, pero no tiene por qué conocer el funcionamiento interno del mismo. Se desea recalcar aquí que el concepto de interfaz es muy diferente al de interface, que se estudiará más adelante

Concepto de clase. Forma general

Una clase sencilla Cuando se escribe un programa en un lenguaje orientado a objetos, se definen previamente las clases que van a describir las características y comportamiento de los objetos que se van a utilizar. Esto sucede de igual manera en C#. Una clase está compuesta por un conjunto de datos –llamados campos o variables miembro- y unos métodos que son funciones que operan sobre esos datos. El conjunto de campos y métodos de una clase constituyen sus miembros. El siguiente es un ejemplo de definición y declaración de una clase: public class Rectangulo { //variables miembro, datos o atributos

int x; int y; private double ancho; private double largo; //funciones miembro o métodos

public Rectángulo (double w, double h) { x = 0; y = 0;

ancho = w; largo = h; } public double CalcularArea() { return ancho*largo; }

}

Objetos Un objeto o instancia es un ejemplar concreto de una clase. En realidad, los objetos son variables de tipo clase. Es importante comprender muy bien la diferencia entre clase y objeto. Clase es la propia definición de un tipo de dato, realizada por el programador o proporcionada por el sistema. Es una abstracción lógica. Sin embargo, el objeto es un ejemplar de una clase, que tiene una existencia física ya que está físicamente en memoria, de la misma forma que una variable de un tipo valor es un ejemplar del tipo de la variable. Por ejemplo:

Page 3: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

3/35

int unaVariable = 7; En esta sentencia, el programador distingue perfectamente entre el tipo de dato –que es un entero, concepto o abstracción lógica- y la variable de ese tipo, que es un “ejemplar” de un entero, con una realidad física, almacenado en memoria y con un valor de 7. De igual forma, hay que distinguir entre clase –tipo de dato- y objeto –ejemplar de ese tipo de dato, de esa clase-. Para crear un objeto o instancia de una clase hay que dar dos pasos:

a ) Crear una variable de tipo referencia que almacene la referencia al objeto. Por ejemplo, Rectángulo unRect; En la sentencia anterior se reserva espacio en la pila o stack para la variable unRect que es una referencia a un objeto de la clase Rectángulo. Después de este paso, unRect no apunta a nada, no referencia ningún objeto. b ) Crear un objeto de la clase y asignarlo a la referencia anterior. unRect = new Rectángulo(5,7); En la sentencia anterior, el operador new seguido de un constructor de la clase crea un objeto de la clase Rectángulo –es decir, reserva el espacio de memoria que sea necesario para un objeto de esa clase- y devuelve una referencia al objeto creado. El símbolo = asigna el objeto recién creado a la referencia unRect. El objeto se almacena en el heap o montón y la referencia en el stack o pila. Lo que se almacena en la variable no es el objeto en sí sino una referencia al objeto que ha sido creado previa asignación del bloque de memoria necesario. Es importante que el lector comprenda que esa variable unRect no es el objeto creado sino una referencia al mismo. El objeto se almacena en el montón.

Objetos

Referencias a objetos de la clase Rectángulo

unRect

Pila x 0

y 0

ancho 5.0

largo 7.0

Montón

doRect x 0

y 0

ancho

largo 347.0

23.0

Page 4: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

4/35

Figura 5.1 En la figura 5.1 puede verse dos referencias unRect y doRect a objetos de la clase Rectángulo. Habitualmente, estos dos pasos se reducen a uno sólo:

Rectángulo unRect = new Rectángulo(5,7); En la expresión anterior se crea una referencia unRect de tipo Rectángulo y se le asigna un objeto creado con el operador new. new crea el objeto físico, que es almacenado en memoria, el objeto, y unRect es una referencia a un objeto de tipo Rectángulo. El operador = asigna el objeto a la referencia. Aunque el objeto y la referencia son distintos, sin embargo es muy habitual hablar del objeto unRect en lugar de decir el objeto de la clase Rectangulo que está referenciado por unRect. Esta manera de hablar no produce ningún tipo de confusión y se utiliza con frecuencia.

Forma general de una clase Una clase es una estructura de datos que puede contener miembros dato (constantes, y campos, como x, y, ancho y largo), funciones miembro (métodos, propiedades, indexadores, eventos, operadores, constructores y destructores como CacularArea() y Rectángulo()) y tipos anidados. Las clases se declaran utilizando la palabra clave class. La estructura general de una clase es la siguiente: [atributos] [modificadores] class identificador [:lista-clases-base] { //cuerpo de la clase //Declaraciones de los miembros de la clase } Una declaración de clase consiste en un conjunto opcional de atributos –se estudiarán más adelante- seguidos por un conjunto también opcional de modificadores de clase, por la palabra reservada class y un identificador que da nombre a la clase. Realmente, la sintaxis es muy parecida a Java o C++. Después puede indicarse la clase base de la que deriva o/y las interfaces que implementa, separadas por comas. Por ejemplo class MiPrimeraClase { //Cuerpo de la clase }

En C# sólo se permite herencia simple, es decir, sólo puede heredarse desde una clase. Sin embargo, como en Java, se puede implementar más de una interface.

Page 5: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

5/35

Los posibles modificadores que puede tener una clase son new, public, protected, internal, private, abstract y sealed.

Los modificadores public, protected, internal y private son modificadores de control de acceso a la clase. Estos modificadores determinan la visibilidad de los miembros de una clase. Se realizará una explicación detallada de los modificadores, más adelante, en este mismo capítulo.

Aunque se volverá a recalcar al hablar de los tipos de acceso, en este libro se seguirán la recomendaciones de Microsoft respecto a cómo escribir los nombres de los miembros de una clase.

• Se escriben con minúsculas los nombres de parámetros y campos privados o protegidos de una clase. Por ejemplo:

private int unEntero; protected string cadena;

• Los miembros públicos, las clases, etc, se escriben con mayúsculas. Por ejemplo:

class MiClase{} namespace MiNamespace;

• Las constantes con mayúsculas. Por ejemplo: public const double PI = 3.14;

• Los nombre de una sóla letra con minúsculas:

int x=8; private string s; Los miembros de una clase pueden ser:

• Constructores • Destructores • Constantes • Campos • Métodos • Propiedades • Indexadores • Operadores

A continuación, se estudian cada uno de ellos.

Constantes y miembros de sólo lectura. Las constantes representan valores constantes asociados con la clase. El modificador const indica que un determinado campo es una constante y, por lo tanto, no puede ser modificada a lo largo del programa.

Page 6: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

6/35

Por ejemplo: class Matemáticas { public const double PI=3.141592; } Una constante es un valor que no puede ser cambiado y no se le puede asignar ningún valor. Por eso es imprescindible inicializarla en la propia clase. Esto significa que no podría haberse incluido en la clase anterior una línea como la siguiente: public const double E; Un campo de sólo lectura –readonly- es muy parecido a una constante pero proporciona un poco más de flexibilidad que una constante, ya que permite inicializarlo después –por ejemplo- de algún cálculo. En el siguiente ejemplo, se asigna un valor a un campo de este tipo en el constructor de la clase: class Personal { public readonly string password; public Personal(string unaPalabra)

{ password=unaPalabra; }

}

Campos. Los campos de una clase almacenan información del objeto de la clase que se ha creado. Son aquéllas características que diferencian dos objetos de una misma clase y determinan su apariencia o estado. El acceso a los campos o miembros dato puede ser directo o bien a través de métodos o propiedades, dependiendo de la visibilidad de los campos –es decir, de los modificadores de cada variable. Los campos de un objeto se invocan con el operador punto (.). C# permite definir como campos tanto tipos valor como tipos referencia. En el primer caso, los objetos que se creen almacenarán una copia de las variables de tipo valor y en el segundo, una referencia a los objetos. También permite definir otra clase de miembros como propiedades, delegates, etc... Los campos de una clase se denominan a veces, campos o variables de instancia o de objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia u objeto contiene su propia copia de cada campo o variable. Los datos de un objeto son propios y están separados –incluso físicamente- de los datos de otro objeto. Por esta razón, los cambios que se realicen en los campos de un objeto no afectan para nada a los campos de los demás objetos, aunque sean del mismo tipo de clase. Esto no sucede así con los campos de clase, porque todos los objetos comparten la misma variable físicamente. Por ejemplo: using System;

Page 7: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

7/35

public class Rectangulo { //variables miembro, datos o atributos

int x; int y; private double ancho; private double largo; //funciones miembro o métodos

public Rectángulo (double w, double h) { x = 0; y = 0;

ancho = w; largo = h; } public Set_Rectángulo(int x0,int y0,int w,int h) { x = x0; y = y0; ancho = w; largo = h; } public double CalcularArea() { return ancho*largo; } public void Imprimir() { Console.WriteLine(“Coordenadas: x={0}, y={1}”,x,y}; Console.WriteLine(“Ancho={0}, Largo={1}”,ancho,largo}; }

} public static void Main() { Rectangulo unRect=new Rectangulo(55,44); unRect.Set_Rectangulo(10,15,100,58);

unRect.Imprimir(); int area=unRect.CalcularArea(); Console.WriteLine(“Area={0}”,area);

} La salida de este programa es: Coordenadas: x = 10, y = 15 Ancho=100, Largo=58 Area=5800 Las variables de instancia se declaran en la clase pero sus valores se inicializan y cambian en los objetos. Además de las variables de instancia hay variables de clase. Se explicarán con detalle más adelante, en este mismo capítulo.

Page 8: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

8/35

Métodos.

Forma general Los métodos de una clase son porciones o bloques de código a los que los objetos de esa clase pueden llamar para que sean ejecutados. Generalmente operan sobre los campos. Las clases no tiene limitación en cuanto al número de métodos. Los métodos pueden tener parámetros y devolverlos. Si un método no devuelve nada, se utiliza la palabra void. La estructura general de un método es: [modificadores] tipo_de_retorno nombreDelMetodo (Parámetros_opcionales) { //cuerpo del método } Los métodos de un determinado objeto se invocan con el operador punto (.). Todo método tiene que tener un nombre (nombreDelMetodo); ninguno, uno o varios parámetros con modificadores también opcionales; puede devolver cualquier tipo de dato u objeto (tipo_de_retorno), incluso los definidos por el programador; y unos modificadores opcionales. Aunque puede haber más, ahora se va a explicar únicamente los que definen método estáticos –static-.

Miembros estáticos Hay ocasiones en los que un miembro de una clase, ya sea campo o método, hace más referencia a la clase –es decir, a todos los objetos de la clase- que a cada objeto en concreto. Suponga –por ejemplo- que se desea llevar una cuenta del número de objetos de una determinada clase en un programa. C# proporciona un camino para crear un miembro que pueda utilizarse por sí mismo sin referirse a una instancia u objeto específico. Para ello, en su definición, el miembro ha de ir precedido por el modificador static. Cuando se declara un miembro como estático, se puede acceder a él sin haber creado ningún objeto en particular. Esto es lo que sucede con el método Main() que debe ser declarado como static porque tiene que ser invocado al inicio del programa, es decir, cuando todavía no se ha creado ningún objeto de ninguna clase. Al tener este modificador, el método Main() puede ser invocado sin necesidad de crear previamente ningún objeto. Los campos declarados como static son variables globales. Cuando se crean objetos de una clase que tiene un campo static, no se hace ninguna copia de él. Todos los objetos de esa clase comparten una única copia del mismo campo. Si uno de ellos lo modifica, quedará modificado para todos ellos. por esta razón, este tipo de miembros se denominan campos o métodos estáticos o de clase o , en contraposición a los métodos o campos de objeto o de instancia.

Page 9: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

9/35

Fuera de la clase en la que han sido definidos, los métodos y campos estáticos se pueden utilizar independientemente de cualquier objeto. Únicamente es necesario especificar el nombre de la clase seguido del operador punto y del nombre del miembro, de la forma: NombreDeLaClase.metodoStatic( ); NombreDeLaClase.campoStatic; A continuación se propone un ejemplo que puede servir para aclarar este concepto: Se trata de diseñar una clase Alumno en la cual se incluya la nota media para ingresar en la universidad y que tenga en cuenta la “nota de corte” para entrar como alumno en la escuela de ingeniería industrial. using System; namespace ConsoleApplication3 { public class Alumno { double nota; public static double notaIngenieros=6; public Alumno(double nota) { this.nota=nota; } public bool estaAdmitido() { return (nota>=notaIngenieros); } public void ImprimirResultado(string nombre) { if(estaAdmitido()) Console.WriteLine("{0} está admitido",nombre); else Console.WriteLine("{0} no está admitido",nombre); } } class MiClase { static void Main(string[] args) { Alumno juan=new Alumno(6.5); Alumno jorge=new Alumno(5.2); Console.WriteLine("Nota de corte:{0}",

Alumno.notaIngenieros); juan.ImprimirResultado("Juan"); jorge.ImprimirResultado("Jorge"); Console.WriteLine("\n"); Alumno.notaIngenieros=7; Console.WriteLine("Nota de corte:{0}",

Alumno.notaIngenieros); juan.ImprimirResultado("Juan"); jorge.ImprimirResultado("Jorge"); } }

Page 10: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

10/35

} La salida de este programa es: Nota de corte: 6 Juan está admitido Jorge no está admitido Nota de corte: 7 Juan no está admitido Jorge no está admitido Puede verse que para llamar al miembro estático, al ser un miembro de clase, hay que invocarlo a través del nombre de la clase. En C# no puede llamarse a un método o campo estático a través del nombre del objeto –como en Java-, porque daría error. Así, notaIngenieros se manipula en la línea: Alumno.notaIngenieros=7; Una vez ejecutada esta sentencia, el miembro estático cambia. Y lo hace en todos los objetos. Hay una sola copia de este miembro, aunque todos los objetos lo tengan y puedan utilizarlo. En este ejemplo, puede verse la diferencia entre un miembro de clase y uno de instancia, como nota. nota es diferente para cada alumno –cada objeto Alumno tiene su propia copia de su nota, y por ello, cambiar a un alumno la nota no modifica la nota del resto de los alumnos- mientras que notaCorteIngenieros tiene el mismo valor para todos los objetos o instancias. Los miembros estáticos deben inicializarse en la propia clase aunque si no se hace así, se inicializarán con los valores por defecto. Los métodos declarados como static tienen algunas restricciones:

a) Sólo pueden llamar a métodos static. b) Solamente pueden acceder a campos static. c) No pueden referirse a this.

En resumen: aunque las clases se definen para poder crear objetos a partir de ella, una clase puede contar con miembros estáticos y ser usada directamente, sin crear objeto alguno. Mediante el uso de miembros estáticos es posible mantener información compartida entre todos los objetos de una clase. Dichos miembros no pertenecen a un objeto en particular, sino que son compartidos por todos. Por lo tanto, este miembro se crea con el primer objeto y es compartido por todos.

Modificadores de los parámetros Es en el paso de parámetros donde se registran las mayores diferencias con Java o C++. En C# hay cuatro alternativas para el paso de parámetros. Para identificar el tipo de paso, el parámetro va precedido en todos los casos excepto en uno, de una palabra

Page 11: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

11/35

reservada. Dicha palabra se debe utilizar tanto en la definición del método como cuando se invoca al método. A continuación se van a estudiar las distintas posibilidades:

• Paso por valor: El método recibe una copia del valor almacenado en la variable original. En su código, el método trabaja con la copia -puede transformarla o modificarla- y pero no puede cambiar la variable original. Este es el único de los cuatro tipos de paso de parámetros que no se identifica por ninguna palabra reservada.

Por ejemplo:

• Paso por referencia: La palabra que identifica este tipo de paso de parámetros es ref. En este caso, al método se le pasa una referencia del objeto o la misma variable –no una copia-, de tal forma que cualquier modificación del objeto o variable se produce en el original porque se trabaja con ella.

Por ejemplo

• Parámetro de salida: La palabra que precede al parámetro es out. Básicamente, es igual al paso por referencia, aunque en este caso el valor que se pasa al método no tiene ninguna importancia, porque ese parámetro va a ser utilizado realmente para devolver un dato o para asignarle un valor a una determinada variable u objeto. Es necesario asignar un valor al parámetro –en el cuerpo del método- antes de utilizarlo. En caso contrario el compilador generará una excepción. No se puede tampoco consultar antes de haberle asignado uno en el propio cuerpo del método.

Por ejemplo:

• Listas variables de parámetros: La palabra que identifica este paso es params. params debe ir seguida del tipo de parámetro y los corchetes [] vacíos, que declaran que se desconoce el número de parámetros que se va a pasar. Si los parámetros no son del mismo tipo se usa el tipo object ya que todos los tipos son objetos.

Por ejemplo

Control de acceso Un programador no necesita conocer el funcionamiento interno de una clase para utilizarla, de igual forma que para utilizar un coche no es necesario conocer el funcionamiento interno del motor y basta con saber cómo “comunicarse” con el coche –acelerar para aumentar la velocidad, girar el volante para cambiar de dirección, etc-. Le es suficiente con saber cómo utilizar un objeto de esa clase (es decir, cómo comunicarse con él) para utilizarlo. Para proteger a los miembros de una clase de modificaciones no deseadas y para garantizar que tomen valores lógicos y acordes con la naturaleza misma de los datos, se introduce el concepto de encapsulación de los datos. Por esta razón, los miembros de una clase van precedidos de un modificador que define la “visibilidad”, el modo de acceso al miembro de la clase.

Page 12: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

12/35

En líneas muy generales, se puede decir que la encapsulación consiste en definir los campos de datos como privados para protegerlos y los métodos como públicos para que puedan manipular los datos. En general, la encapsulación es una manera común de escribir el código de una clase. Se mantienen los datos ocultos al exterior de la clase para que no se puedan utilizar desde el exterior y se escriben una serie de miembros públicos –que pueden manipular cualquier campo de esa clase- que permitan al programador manipular esos datos –o algunos datos- desde el exterior, desde su programa. En general, el interfaz –no el interface- de una clase está constituido por el conjunto de métodos públicos de una clase y es el medio por el que el programador puede “comunicarse” con los objetos de esa clase: puede modificar y manipular los datos, cambiar sus campos, enviarle mensajes, etc. No es necesario que el programador tenga acceso a los detalles de implementación de la clase. Le es suficiente con conocer cómo se utilizan los métodos públicos: qué mensajes se pueden enviar al objeto a través de ellos, y cómo responden los objetos a un determinado mensaje, es decir, qué devuelven. En resumen, le basta con conocer el interfaz de la clase.. Los modificadores de acceso a los miembros de una clase permiten ocultar, proteger y encapsular los datos de una clase. Aunque posteriormente se hablará del resto de los modificadores, ahora se explica el acceso a los miembros públicos y privados de una clase. Un miembro que no tiene un modificador de acceso es, por defecto, private. Por ejemplo: public class UnaClase { int a; private int b; public int c; public void Imprimir() { Console.WriteLine("a={0}, b={1},C={2}",a,b,C); } } class MiAplicacion { static void Main(string[] args) { UnaClase clase=new UnaClase();

//Los objetos de UnaClase no pueden acceder //a los miembros privados de UnaClase. //las dos siguientes líneas darán error al compilar clase.a = 10; clase.b = 11;

//Los objetos de UnaClase pueden acceder //a los miembros públicos de UnaClase. //Solo se puede llamar desde el exterior de una clase //a sus miembros publicos clase.c = 12; clase.Imprimir();

Page 13: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

13/35

} } Las líneas

clase.a = 10;

clase.b = 11; no pueden compilarse –deben ser comentadas para que el proceso de compilación las omita- porque no se puede invocar un miembro privado desde el exterior de la propia clase; ni siquiera los objetos de la clase UnaClase pueden hacerlo. Esos miembros sólo se pueden manipular, invocar o llamar desde el mismo código de definición de la clase. Se puede decir que las funciones miembro de la clase UnaClase pueden acceder a miembros públicos y privados de la clase. Los objetos de la clase UnaClase pueden acceder sólo a miembros públicos de UnaClase, como puede verse en las siguientes líneas:

clase.c = 12; clase.Imprimir();

Sin embargo los miembros de la clase pueden acceder a los miembros públicos y privados de la clase, pero siempre desde el interior –desde el propio código de definición- de la clase. Por ejemplo, vemos que el método Imprimir() puede acceder sin problemas a los miembros de la clase. La salida del programa es a=0,b=0, c=12;

Sobrecarga de métodos Muchas veces se ha utilizado en este libro la sobrecarga de métodos pero sin decirlo explícitamente. Por ejemplo, para imprimir en pantalla un valor entero, se puede hacer algo parecido a: int x = 6; Console.WriteLine(x}; Si se desea imprimir una cadena, el código puede ser: String unaCadena = “Hola Mundo”; Console.WriteLine(unaCadena}; ¿Cómo es esto posible? ¿Porqué el método Console.WriteLine() toma dos parámetros de tipo distinto? Si el método espera un string en el primero de los casos generaría un error al compilar porque no se realiza un casting implícitamente de int a string. Es parecido lo que ocurriría en el segundo de los casos. La explicación es que hay dos métodos diferentes de nombre Console.WriteLine(), uno de los cuales toma como parámetro un entero y el otro una cadena. Incluso, se puede utilizar este método con un número de parámetros diferente. Por ejemplo:

Page 14: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

14/35

String unaCadena = “Hola Mundo”; Console.WriteLine(“La cadena es {0,}”,unaCadena}; En C#, como en otros lenguajes de programación orientada a objetos, se puede sobrecargar métodos. La sobrecarga de métodos consiste en declarar y definir varios métodos distintos con el mismo nombre. Cada uno de estos métodos se define de forma diferente. En el momento de la ejecución el compilador llama a uno u otro dependiendo del tipo de los argumentos del método y/o del número de parámetros que se utilizan. Por ejemplo, se puede definir varias funciones para calcular el valor absoluto de una variable, todas con el mismo nombre pero cada una con un argumento diferente y devolviendo un valor distinto. Para sobrecargar un método es necesario que los dos métodos difieran al menos en el tipo de alguno de sus argumentos o en el número de ellos. No basta con que tengan valores de retorno diferentes. Por ejemplo: using System; namespace Sobrecarga { class UnaAplicacion { public static int Abs(int n) { if(n>=0) return n; else return -n; } public static double Abs(double d) { if(d>=0) return d; else return -d; } static void Main(string[] args) { int unEntero=-2; double unDouble=3.34; Console.WriteLine("Abs({0})={1}",unEntero,Abs(unEntero)); Console.WriteLine("Abs({0})={1}",unDouble,Abs(unDouble)); } } } La salida de este programa es: Abs(-2) = 2 Abs(3.34) = 3.34

Page 15: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

15/35

En este ejemplo se observa cómo el compilador puede distinguir el código que debe utilizar porque el tipo de argumento es diferente en los dos métodos. En el siguiente ejemplo, el compilador puede distinguirlos porque el número de argumentos que utiliza en las llamadas es diferente –aunque en este caso también el tipo-. using System; namespace UnNamespace { public class MiClaseApp { public static void Imprimir(string str) { Console.WriteLine("La cadena es {0}",str); } public static void Imprimir(int x, double y) { Console.WriteLine("x={0},y={1}",x,y); } static void Main(string[] args) { int x=8; double y=9.34; string unaCadena="Hola Mundo"; Imprimir(x,y); Imprimir(unaCadena); } } } La salida de este programa es : x=8, y=9.34 La cadena es Hola Mundo Como se verá más adelante, los operadores se pueden también sobrecargar, aunque tienen una estructura un poco diferente a la sobrecarga de métodos.

Constructores. El constructor es un método especial que permite inicializar un objeto de una clase en el momento que éste se está creando. Es decir, da unos valores iniciales a los distintos campos del objeto en el momento en que se crea. Un constructor debe:

a) Tener el mismo nombre de la clase b) Ser público c) No tener ningún retorno –ni siquiera void-.

Realmente, lo que ocurre es que inmediatamente después de que se crea el objeto, el compilador invoca al constructor. El funcionamiento es igual que un método. En el ejemplo de la clase Rectángulo, ya se ha utilizado un constructor de la clase.

public Rectángulo (double w, double h) {

Page 16: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

16/35

x = 0; y = 0; ancho = w; largo = h;

} Para crear un objeto de la clase Rectangulo se utilizaba la instrucción: Rectangulo unRect = new Rectangulo(55,44); Esta sentencia, después de reservar memoria para el objeto –para cada uno de sus campos- ejecuta el código del constructor. Inicializa los campos x, y, ancho y largo con los valores 0,0, 55 y 44 respectivamente. Además se puede distinguir entre los constructores de instancia y los constructores estáticos (static). Los constructores de instancia implementan las acciones necesarias para inicializar las instancias de la clase, es decir, los objetos. Los constructores estáticos (static): implementan las acciones necesarias para inicializar la clase en sí.

Sobrecarga de un constructor Un constructor puede estar sobrecargado. De esta manera se proporciona una gran flexibilidad a la hora de inicializar los objetos de una clase cualquiera. La sobrecarga de un constructor es muy parecida a la sobrecarga de un método. Se puede escribir para una clase tantos constructores como se desee. Sin embargo, los constructores deben diferenciarse entre sí o en el número de parámetros o al menos, en el tipo de parámetro. El compilador en tiempo de ejecución diferenciará entre ellos y utilizará el que se ajuste en número y tipo al constructor que se utilice en el programa. En el siguiente ejemplo se ilustra esta idea: using System; namespace Matematicas { class Rectangulo { int x0; int y0; int ancho; int largo; public Rectangulo() { x0=y0=0; ancho=10; largo=10; } public Rectangulo(int x0,int y0,int ancho,int largo) { this.x0=x0;

Page 17: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

17/35

this.y0=y0; this.ancho=ancho; this.largo=largo; } public Rectangulo(int ancho,int largo) { x0=0; y0=0; this.ancho=ancho; this.largo=largo; } public Rectangulo(int lado) { x0=0; y0=0; ancho=lado; largo=lado; } public void Imprimir(string nombreDelRectangulo) { Console.WriteLine("Datos del Rectangulo{0}"

,nombreDelRectangulo); Console.WriteLine("x0={0},y0={1},ancho={2},largo={3}"

,x0,y0,ancho,largo); Console.WriteLine(); } } class ClaseApp { static void Main(string[] args) { Rectangulo rect1=new Rectangulo(); rect1.Imprimir("Primer Rectangulo"); Rectangulo rect2=new Rectangulo(100); rect1.Imprimir("Segundo Rectangulo"); Rectangulo rect3=new Rectangulo(100,200); rect1.Imprimir("Tercer Rectangulo"); Rectangulo rect4=new Rectangulo(20,30,200,400); rect1.Imprimir("Cuarto Rectangulo"); } } } La salida de este programa es:

Page 18: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

18/35

Figura 5.2 Hay que tener en cuenta que si se define un constructor cualquiera para una clase, el constructor por defecto queda anulado. Así, en el anterior ejemplo no hubiera sido posible utilizar Rectángulo() si no se hubiera sobrecargado.

Inicialización de miembros por defecto

Si no se define ningún constructor, el compilador define un constructor denominado constructor por defecto. Dicho constructor no tiene código ni parámetros. Su única misión es inicializar cada uno de los campos con sus valores por defecto. Dichos valores dependen del tipo de dato con el que se esté trabajando. Las inicializaciones por defecto para los distintos tipos es la siguiente:

- Para todos los tipos simples, el valor por defecto es 0.

- Para sbyte, byte, short, ushort, int, uint, long, y ulong, el valor por defecto es 0.

- Para char, el valor por defecto es '\x0000' - Para float, el valor por defecto es 0.0f. - Para double, el valor por defecto es 0.0d. - Para decimal, el valor por defecto es 0.0m. - Para bool, el valor por defecto es false.

- Para un tipo enum, el valor por defecto es 0. - Para un tipo struct, al igual que la clase, el valor por defecto es el resultado de dar

el valor por defecto a todos sus campos. Por ejemplo: using System; public class Persona {

Page 19: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

19/35

private int edad; private string nombre; // Constructor por defecto public Persona () { nombre = "N/A"; } // Constructor public Persona (string nombre, int edad) { this.nombre = nombre; this.edad = edad; } public void Imprimir() { Console.WriteLine("{0} tiene {1} años.", nombre, edad); } } public class UnaAplicacion { public static void Main() { // Se crean los objetos // Se deben crear utilizando el operador new: Persona persona1 = new Persona ("Patricia", 11); Persona persona2 = new Persona ("Maria", 10); // Crea un objeto utilizando el constructor por defecto: Persona persona3 = new Persona (); // Imprimimos los resultados: Console.Write("Persona #1: "); persona1.Imprimir (); Console.Write("Persona #2: "); persona2.Imprimir (); Console.Write("Persona #3: "); persona3.Imprimir (); } }

Referencia this Si una variable local tiene el mismo nombre que un miembro de una clase entonces lo oculta. Para estas ocasiones, o para otras en la que por diversas circunstancias interesa referirse en el código de una clase al propio objeto, se utiliza this, que es una referencia implícita al propio objeto. Por ejemplo: class Rectángulo { int ancho;

int largo; public Rectángulo(int ancho, int largo)

Page 20: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

20/35

{ this.ancho=ancho; this.largo=largo; }

} En este ejemplo, los parámetros ancho y largo del constructor ocultan los campos de la propia clase porque tienen el mismo nombre. Para evitar este problema y que muchas veces permite que el código sea más legible, se utiliza this.

Destructores y recolector de basura. Los destructores contienen el código que se ha de ejecutar cuando se destruye un objeto, una instancia de una clase. Su forma general es:

[atributosopt] ~ nombreDeLaClase( ) { //Codigo }

Los atributos son opcionales. El nombre debe coincidir con el de la clase, pero debe ir precedido del símbolo ~ (ALT+126) El funcionamiento de los destructores no se parece al de C++. Realmente, es el código que se ejecuta cuando un objeto se destruye. Es muy parecido al método finalize de Java. El destructor es llamado por el recolector de basura cuando el objeto va a ser destruido. De hecho es un alias del método finalize. Se mantiene por comodidad al migrar.

Propiedades. Las propiedades constituyen una importante aportación de C# y de uno de los autores de este lenguaje, Anders Hejlsberg. Probablemente, algunos lectores que hayan utilizado Delphi o C++ Builder de Borland, el concepto de propiedad les resultará familiar, ya que Hejlsberg es el diseñador de la VCL que es la librería en la que se apoyan estos dos estupendas herramientas visuales de programación. Las propiedades permiten acceder a los campos privados de una clase de una manera controlada y sencilla. Esto resulta especialmente útil, ya que es importante no permitir que un determinado campo pueda adquirir valores no deseados. Además, las propiedades permiten dotar a los campos de la característica de ser de lectura o escritura únicamente. Las propiedades definen atributos con nombre (campos, realmente) y las acciones asociadas con la lectura y escritura de esos atributos. Antes de proseguir con el concepto de propiedad, es importante que el lector comprenda bien los dos procesos distintos –accesos- que pueden llevarse a cabo en un campo de una clase: el proceso de lectura y el proceso de escritura.

Page 21: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

21/35

Por ejemplo: using System; namespace Propiedades { class Rectangulo { public int Ancho; public int Largo; } class MiAplicacion { static void Main(string[] args) { Rectangulo unRect=new Rectangulo(); unRect.Ancho=10; unRect.Largo=25;

Console.WriteLine("Ancho={0}, Largo={1}",unRect.Ancho,unRect.Largo);

} } } En este programa se puede observar cómo se asignan valores –10 y 25- a los campos públicos Ancho y Largo del objeto unRect. La asignación se puede considerar como un proceso de escritura en el campo. Sin embargo, en la sentencia en la que se imprimen los datos se está consultando un campo o en un proceso de lectura del campo. En este ejemplo se han considerado dos campos públicos para ganar en sencillez. Sin embargo y como ya se ha explicado en varias ocasiones no es lo ideal, porque los campos deben tener un acceso restringido, tanto de lectura como de escritura. Piense el lector, por ejemplo, si se hubiera dado una de las dos dimensiones con un valor negativo. No tendría sentido. El hecho de controlar los procesos de lectura y escritura es muy importante. Se puede hacer escribiendo métodos apropiados y de hecho, en los lenguajes orientados a objetos se venía haciendo así hasta ahora. Sin embargo, esto puede ser, en ocasiones, tedioso. Las propiedades constituyen una manera fácil y elegante de hacerlo. La declaración de una propiedad consta de una parte donde se define el proceso de lectura –denominado acceso get- y otra en la que se define el proceso de escritura o acceso set. El acceso get contiene el código que se ha de ejecutar en la consulta o lectura de la propiedad y por lo tanto del campo. Debe terminar siempre con la sentencia return o throw. Además el flujo de control no puede salir del cuerpo del acceso. Por ejemplo: private string nombre; // Campo nombre public string Nombre // Propiedad Nombre { get

Page 22: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

22/35

{ return nombre; } } Para utilizarlo: Persona unaPersona = new Persona(); ... Console.Write(unaPersona.Nombre); // Se invoca el acceso get. El acceso set contiene el código que se ha de ejecutar al escribir en la propiedad y por lo tanto en un campo. El parámetro value es el valor que se le da a la propiedad. Generalmente en este campo se suele implementar el código de validación de acceso a la propiedad. Ejemplo: public string Nombre { get { return nombre; } set { nombre = value; } } Un uso posible es, por ejemplo: unaPersona.Nombre = "Macarena"; // Se invoca el acceso set Ahora se adjunta un ejemplo completo: using System; namespace Propiedades { class Rectangulo { int ancho; int largo; public Rectangulo(int w, int h) { ancho=w; largo=h; } public int Ancho { get { return ancho; } set {

Page 23: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

23/35

if(value<0) ancho=0; else ancho=value; } } public int Largo { get { return largo; } set { if(value<0) largo=0; else largo=value; } } } class MiAplicacion { static void Main(string[] args) { Rectangulo unRect=new Rectangulo(13,26); unRect.Ancho=10; unRect.Largo=25; Console.WriteLine("Ancho={0}, Largo={1}",

unRect.Ancho,unRect.Largo); } } } Realmente el código es el mismo que el anterior, pero se permite la protección de los datos de la clase y además se controla la asignación de valores a los campos. Las propiedades permiten ocultar detalles de la implementación, pero también permiten establecer el hecho de que un campo sea de sólo lectura o de sólo escritura. Esto se consigue de la siguiente manera:

a) Para establecer un campo como de sólo lectura se debe implementar únicamente el acceso get.

b) Para establecer un campo como sólo de escritura se debe implementar sólo el acceso set.

c) Para establecer un campo como de lectura y escritura se debe implementar los accesos get y set.

Por ejemplo: using System; namespace Accesos {

Page 24: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

24/35

class Persona { string nombre; string direccion; public Persona(string str) { nombre=str; } public string Direccion { get { return direccion; } set { direccion=value; } } //Definimos una propiedad de sólo lectura public string Nombre { get { return nombre; } } } class AccesosApp { static void Main(string[] args) { Persona p=new Persona("Patricia");

//ERROR en la siguiente línea. //El campo es de solo lectura

//unaPersona.Nombre="Juan"; p.Direccion="Gran Vía 66"; Console.WriteLine("Nombre={0}, Direccion={1}",

p.Nombre,p.Direccion="Gran Vía 66"); } } } La línea unaPersona.Nombre="Juan"; está comentada porque es una propiedad de sólo lectura. No puede escribirse ningún dato en la propiedad. Sólo se puede hacer al crear el objeto, a través del constructor. A continuación se proponen una serie de ejemplos que tienen que ver con la herencia y con la sobrecarga. Si el lector no conoce estos dos temas, es mejor que los deje para una lectura posterior. Ejemplo using System; public class ClaseBase

Page 25: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

25/35

{ private string nombre; public string Nombre { get { return nombre; } set { nombre = value; } } } public class ClaseDerivada : ClaseBase { private string nombre; // importante: se utiliza el modificador new // mas adelante se explica el significado de este modificador public new string Nombre { get { return nombre; } set { nombre = value; } } } public class ClasePrincipal { public static void Main() { ClaseDerivada d = new ClaseDerivada (); d.Nombre = "Germán"; // Propiedad de la clase derivada Console.WriteLine("Nombre en la clase derivada: {0}",d.Nombre); ((ClaseBase)d).Nombre="Itxaso"; // Propiedad de la clase base Console.WriteLine("Nombre en la clase base: {0}", ((ClaseBase)d). Nombre); } } Ejemplo 2: using System; namespace Accesos { abstract class Figura { public abstract double Area { get; set; }

Page 26: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

26/35

} class Cuadrado: Figura { public double lado; // Constructor: public Cuadrado(double s) { lado = s; } // Propiedad Area public override double Area { get { return lado * lado; } set { // Dado el area, se calcula el lado lado = Math.Sqrt(value); } } } class Cubo: Figura { public double lado; // Constructor: public Cubo(double s) { lado = s; } // Propiedad Area public override double Area { get { return 6*lado*lado; } set { // Dado el area, calcula el lado lado = Math.Sqrt(value/6); } } } public class ClasePrincipal { public static void Main() { // Introducir el lado: Console.Write("Introduce el lado: "); string ladoString = Console.ReadLine(); double lado = double.Parse(ladoString);

Page 27: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

27/35

// Calculo de las areas: Cuadrado s = new Cuadrado(lado); Cubo c = new Cubo(lado); // Imprime los resultados en pantalla: Console.WriteLine("Area Cuadrado={0}",s.Area); Console.WriteLine("Area Cubo={0}",c.Area); // Introducir el area: Console.Write("Introduzca el area: "); string areaString = Console.ReadLine(); double area = double.Parse(areaString); // Calcular areas: s.Area = area; c.Area = area; // Imprime los resultados en pantalla: Console.WriteLine("Lado del cuadrado={0}", s.lado); Console.WriteLine("Lado del Cubo = {0}", c.lado); } } }

Indexadores. Permiten indexar las instancias de una clase o estructura del mismo modo que un array. Un indexador es una variante de una propiedad que admite un índice. Normalmente se utiliza para asociar cada elemento de la clase o estructura a la que pertenece con cada posible valor del índice. No obstante, se puede programar como se desee la respuesta que se de al uso del indexador. Supóngase una clase PuntoEspacial cuya definición es: class PuntoEspacial { public int x, y, z; ... ... ... } La manera de utilizar sus instancias sería, por ejemplo: PuntoEspacial punto1; punto1.x = 5; punto1.y = 6; punto1.z = 10; Mediante un indexador, es posible asociar el índice 0 a ‘x’, el 1 a ‘y’ y el 2 a ‘z’, de modo que: punto1[0] = 5; equivalga a hacer punto1.x = 5; punto1[1] = 6; equivalga a hacer punto1.y = 6;

Page 28: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

28/35

y punto1[2] = 10; equivalga a hacer punto1.x = 10; La clase PuntoEspacial con el indexador, quedará como sigue: class PuntoEspacial { public int x, y, z; public int this [int indice] { get { switch (indice) { case 0: return x; break; case 1: return y; break; case 2: return z; break;

default: System.Console.WriteLine (“El índice está fuera del rango permitido”);

} } set

{ switch (indice) { case 0: x = value; break; case 1: y = value; break; case 2: z = value; break;

default: System.Console.WriteLine (“El índice está fuera del rango permitido”);

} }

} } Como puede observarse, el modo de declarar el indexador es: public int this [int indice] que quiere decir que es una propiedad pública, que recibe un índice y puede recibir (set) o devolver un entero (get). En realidad, excluyendo el hecho de que recibe un índice, el indexador es una propiedad normal. Si se utiliza para una asignación (value representa el valor asignado) se ejecuta set y si se utiliza para obtener el valor de un elemento de la clase se ejecuta el método get.

Page 29: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

29/35

Operadores. Los operadores se han venido utilizando hasta ahora, se pueden “sobrecargar”, de tal manera que si, por ejemplo, z1 y z2 son dos números complejos, de manera automática el compilador entienda que z1 + z2 es la suma de dos números complejos y por lo tanto debe sumar de una manera diferente. Los operadores que se pueden sobrecargar son:

+ - ! ~ ++ -- True False + - * / % & | ^ << >> == != > < >= <=

En C# es muy sencillo sobrecargar un operador y la sobrecarga es muy parecida a la de C++. Ejemplo: a continuación se va a sobrecargar el operador binario + para la clase Complejo, de modo que sea posible sumar dos objetos de esta clase. using System; namespace NameSpaceDeComplejos { public class Complejo { public int real = 0; public int im = 0; public Complejo(int real, int im) { this.real = real; this.im = im; } public static Complejo operator +(Complejo c1, Complejo c2) { Complejo temporal=new Complejo(c1.real+c2.real,

c1.im + c2.im); return temporal; //podría haberse hecho así //return new Complejo(c1.real + c2.real,

// c1.im + c2.im); } public static void Main() { Complejo z1 = new Complejo(2,3); Complejo z2 = new Complejo(3,4); Complejo z = z1 + z2; Console.WriteLine("Parte Real: {0}", z.real); Console.WriteLine("Parte imaginaria: {0}", z.im); } } }

Page 30: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

30/35

La línea más importante es: public static Complejo operator +(Complejo c1, Complejo c2) Para sobrecargar un operador:

a) El método debe ser public. b) Debe ser static. c) Se debe indicar qué tipo devuelve. En general devolverá un tipo igual a los que

se esté operado, para poder “concatenar” dicha operación: por ejemplo, podría realizarse una operación z1 + z2 + z3.

d) Se debe indicar siempre la palabra reservada operator seguida del operador que se vaya a sobrecargar.

e) Entre paréntesis el o los operandos, especificando sus tipos que se vaya a utilizar. Si el operador es unario, sólo tendrá un operando.

Se propone al lector que complete la clase Complejo sobrecargando el resto de las operaciones binarias (resta, producto y división de números complejos) y sobrecargue algún operador unario, por ejemplo ++ (se podría sobrecargar de tal manera que aumente en una unidad la parte real y deje igual la parte imaginaria). Puede también ser interesante sobrecargar el operador ==, para comparar dos números complejos.

Modificadores de clase. Los modificadores de una clase pueden ser:

new abstract sealed public protected internal private

new. Es posible definir una clase en el interior de otra. Esto es útil muchas veces para ocultarla o bien porque nos interesa utilizarla como miembro de la clase que la contiene. Esto es muy habitual en la programación visual. El modificador new sólo se permite en clases anidadas. Indica que la clase oculta un miembro heredado con el mismo nombre que aquel al que afecta new. En una declaración de clase se permite declarar un miembro con el mismo nombre y parámetros que otro heredado. En tal caso se dice que el miembro derivado “esconde” al miembro de la clase base. Ante la situación comentada el compilador muestra un aviso o warning. Si se quiere evitar tal aviso ha de precederse la declaración del nuevo miembro con el modificador new. Ejemplo:

Page 31: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

31/35

public class MiClaseBase { public int x; public void UnaFuncion(); } public class MiClaseDerivada : MiClaseBase { new public void UnaFuncion (); //mas codigo } No se debe utilizar new y override en la misma declaración, ya que dará un error de compilación. Se ha realizado un ejemplo cuando se han explicado las propiedades. Ejemplo: using System; public class MiClaseBase { public static int x = 55; public static int y = 22; } public class MiClaseDerivada: MiClaseBase { //El miembro ocultaria al de la clase base sin el modificador new new public static int x = 100; public static void Main() { // Imprime en pantalla el valor de x de la clase derivada Console.WriteLine(x); // Acceso al valor oculto x de la clase base: Console.WriteLine(MiClaseBase.x); // Imprime en pantalla el valor de y, que no está oculto Console.WriteLine(y); } }

Modificadores de visibilidad o de acceso

Los modificadores public, protected, internal y private son modificadores de control de acceso a la clase. Estos modificadores determinan la visibilidad de un miembro de una clase. Se realizará una explicación detallada de esos modificadores al estudiar el concepto de herencia.

Su efecto se indica en la tabla 5.1:

Modificador Visibilidad public Acceso Total. protected El acceso está permitido a la clase que contiene al miembro y a

sus tipos derivados.

Page 32: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

32/35

internal El acceso está limitado al proyecto actual. protected internal El acceso está limitado al proyecto actual y a los tipos

derivados de la clase que contiene al miembro. private El acceso está limitado al tipo clase que contiene al miembro. Tabla 5.1 No todos los modificadores pueden utilizarse con todos los tipos, las opciones permitidas son: Tipo Opción por defecto de acceso a los miembros Opciones permitidas enum public None class private public

protected internal private protected internal

interface public None struct private public

internal private

Tabla 5.2 Por ejemplo: using System; namespace MiEspacioDeNombres { public class T1 { public static int unEnteroPublico; internal static int unEnteroInternal; private static int unEnteroPrivado = 0; public class M1 { public static int unEnteroPublico; internal static int unEnteroInternal; private static int unEnteroPrivado = 0; } private class M2 { public static int unEnteroPublico= 0; internal static int unEnteroInternal = 0; private static int unEnteroPrivado = 0; } } public class MainClass { public static int Main() { // Acceso a los campos de T1

Page 33: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

33/35

T1.unEnteroPublico=1; // Acceso ilimitado T1.unEnteroInternal=2; // Accesible solo en proyecto actual // T1.unEnteroPrivado=3;// Error:inaccessible fuera de T1 // Acceso a los campos de M1 T1.M1.unEnteroPublico= 1; // Acceso ilimitado T1.M1.unEnteroInternal=2; //Acceso sólo en el proyecto //actual //T1.M1.unEnteroPrivado=3; // Error: inaccesible fuera de M1 //Acceso a los campos de M2 //T1.M2.unEnteroPublico=1; // Error: inaccesible fuera de T1 //T1.M2.unEnteroInternal=2; // Error: inaccesible fuera de T1 //T1.M2.unEnteroPrivado=3; // Error: inaccesible fuera de M2 return 0; } } }

abstract. Se denomina clase abstracta a aquélla que contiene uno o más miembros abstractos. Este modificador indica que no se pueden crear instancias u objetos de esa clase, porque no está totalmente implementado. Habitualmente se utilizan para definir el interfaz que deben heredar todas sus subclases, ya que todas las clases que derivan de ella deben implementar, de manera obligatoria, todos los métodos que son abstractos en la clase base. Pueden crearse objetos de clases derivadas de una clase abstracta. Para definir una clase como abstracta, debe ir precedida de la palabra abstract Por ejemplo: abstract class MiClaseBase { //codigo con algun miembro abstracto } Entonces, la sentencia MiClaseBase unaClaseBase=new MiClaseBase(); no se compilaría nunca, porque no se pueden crear objetos de una clase abstracta. Sin embargo, supóngase que deriva de esta clase otra –por ejemplo MiClaseDerivada-que implementa sus miembros abstractos: Class MiClaseDerivada : MiClaseBase { //codigo }

Page 34: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

34/35

Gracias al polimorfismo de referencias, es posible que una referencia a un objeto de la clase base, referencie a un objeto de la clase derivada. Esto es cierto, incluso cuando la clase base es abstracta o un interface. Por esta razón, el siguiente código, en la aplicación principal es perfectamente válido: MiClaseBase unaRefBase; MiClaseDerivada unaRefDerivada = new MiClaseDerivada(); UnaRefBase = unaRefDerivada; O bien: MiClaseBase unaRefBase = new MiClaseDerivada(); En el siguiente ejemplo se ha definido una clase abstracta, con una propiedad abstract. Cuando se sobrescribe un miembro abstracto, basta con precederlo de la palabra override. Es decir, para sobrescribir un miembro, éste debe ir precedido o modificado por las palabras reservadas abstract –si es que no lo implementa- o virtual –si lo implementa-. Esto es lo que sucede con el miembro MiMetodo(). Ejemplo: using System; abstract class MiClaseBase // Clase Abstracta { protected int x = 100; protected int y = 150; public abstract void MiMetodo(); // Metodo abstracto public abstract int GetX //Propiedad abstracta { get; } public abstract int GetY // Propiedad abstracta { get; } } class MiClaseDerivada: MiClaseBase { public override void MiMetodo() { x++; y++; } public override int GetX // overriding property { get { return x+10; } } public override int GetY // overriding property {

Page 35: Clases. - UPV/EHUehu.eus/mrodriguez/archivos/csharppdf/Lenguaje/Clases.pdf · objeto en contraposición a las variables estáticas o de clase. Se llaman así porque cada instancia

Marco Besteiro y Miguel Rodríguez Clases

35/35

get { return y+10; } } public static void Main() { MiClaseDerivada mC = new MiClaseDerivada (); mC.MiMetodo(); Console.WriteLine("x = {0}, y = {1}", mC.GetX, mC.GetY); } }

sealed. El modificador sealed hace que la clase a la que modifique no pueda ser derivada. Algunas clases que se suministran con la librería .NET utilizan este modificador. Ejemplo: using System; sealed class MiClaseCerrada { public int x; public int y; } class MainClass { public static void Main() { MiClaseCerrada mC = new MiClaseCerrada(); mC.x = 110; mC.y = 150; Console.WriteLine("x = {0}, y = {1}", mC.x, mC.y); } }