Sunteți pe pagina 1din 70

Estructuras de control

Hay dos maneras de cambiar el rumbo de ejecución de un programa, estos


pueden ser saltos incondicionales y saltos condicionales. En este capítulo se
describen algunas de estas sentencias. Algunas son muy similares a las
existentes en otros lenguajes, como las sentencias if, for, while, etc. y otras,
como foreach, throw o continue, son algo más específicas.

[editar]Saltos incondicionales

Las instrucciones de un programa se ejecutan sentencia por sentencia


empezando desde el método o función principal llamado Main() hasta
terminar con el programa. El programa sin embargo, tomará otros rumbos
incondicionalmente en dos oportunidades: 1. Cuando encuentre la llamada a
otros métodos (Ejemplo 4.1) y 2. Con el uso de las palabras claves como
goto, break, continue, return y throw las cuales se discutirán más adelante.

Ejemplo 4.1 - Salto incondicional a otra función

using System;

namespace Ejemplos{

class Ejemplo4_1{

static void Main(){

Console.WriteLine ("Esta parte se ejecuta primero");

LlamadaOtraFuncion();

Console.WriteLine ("Esta parte se ejecuta al final");

static void LlamadaOtraFuncion(){

Console.WriteLine ("Ha salido del método Main()");

}
En el ejemplo anterior el programa ejecuta sentencia por sentencia el método
principal Main() hasta que encuentra la llamada a otro método. Después de
que el método llamado haya terminado el método Main continuará con la
ejecución de las sentencias restantes.

[editar]La sentencia goto

En los inicios de los lenguajes de programación la sentencia goto fue la más


popular para ir de un lugar a otro dentro del programa. Sin embargo esto
creaba una tremenda confusión al momento de diseñar la aplicación. Si el
programador quería hacer un esquema de como funcionaba dicha aplicación,
se veía con un laberinto tipo espagueti de líneas y símbolos conectados entre
si. Es por esto que esta sentencia es un poco "problemática" y fuera de
"moda" entre los lenguajes de programación modernos. C# sin embargo
soporta esta sentencia. Os recomendamos no utilizarla a menos que sea
necesario o si os sentís cómodos haciéndolo, pero cuando os cree un
laberinto difícil de depurar, no digáis que no os advertimos de no utilizarla.
Hay muchas otras mejores maneras de cumplir con el mismo propósito (la
sentencia while por ejemplo es una de ellas), las cuales son más elegantes y
más sencillas de depurar.

La sentencia goto funciona de la siguiente manera:

Primero se crea una etiqueta al inicio de cierto bloque de código y después


en otro lugar podemos saltar hacia esa etiqueta usando la palabra clave goto.
El siguiente ejemplo ilustra la sentencia goto:

using System;

namespace Ejemplos

class Ejemplo4_2

static void Main()

int contador=0;

REPETIR:

Console.WriteLine ("Esta línea se repetirá 100 veces, esta es la linea


numero: {0}", contador);

if (contador++ < 100)


goto REPETIR;

Console.WriteLine ("Despues de que el contador sea igual o mayor


que 100 se imprimirá esta línea");

Esta sentencia es un ejemplo de salto incondicional ya que por si solo saltará


a la etiqueta seleccionada incondicionalmente.

[editar]Saltos condicionales

Los saltos condicionales sirven para ejecutar cierto código solamente si se


cumple con alguna condición. Entre otros tenemos:

[editar]Instrucción if

Esta sentencia sirve para ejecutar unas instrucciones en caso de que se


cumpla determinada condición. La forma completa de la instrucción if es

if( condición ) {

instrucciones;

...

else {

instrucciones;

...

donde la cláusula else es opcional. Si la condición es verdadera, se


ejecutarán las instrucciones dentro del bloque if, mientras que si es falsa, se
ejecutará el bloque else. El valor que controla la sentencia if debe ser de tipo
bool. El siguiente ejemplo

//programa que determina si un valor es positivo o negativo

using System;
class InstruccionIf{

public static void Main()

double d;

Console.WriteLine("Introduce un numero");

d = Double.Parse( Console.ReadLine() );

if( d>0 )

Console.WriteLine("El numero {0} es positivo", d);

else

Console.WriteLine("El numero {0} es negativo", d);

te pide que introduzcas un número y dependiendo de si se cumple que dicho


número es mayor que cero (condición), se ejecuta un bloque u otro.

La sentencia d = Double.Parse( Console.ReadLine() ); tal vez requiera algo de


explicación adicional. En realidad, con Console.ReadLine() estamos leyendo
lo que el usuario introduce por pantalla, que es una cadena de caractéres, y
con Double.Parse lo que hacemos es interpretar esa cadena de caractéres y
convertirla en un tipo numérico double, de forma que tendrá el valor del
número que introduzcamos por la consola.

Las intrucciones if se pueden anidar, y existe también una extensión de la


sentencia if, la sentencia if-else-if. Su formato es el siguiente:

if( condicion1 )

instrucciones;

else if( condicion2 )

instrucciones;

...

else

instrucciones;

Las instrucciones condicionales se evalúan de arriba a abajo. Tan pronto


como se encuentra una condición true, se ejecuta la instrucción asociada con
ella, y el resto de la escalera se omite. Si ninguna de las condiciones es true,
se ejecutará la última instrucción else. La última instrucción else actúa como
condición predeterminada, es decir, si no funciona ninguna de las otras
pruebas condicionales, se realiza esta última instrucción. Si no existe esta
instrucción else final y el resto de de las condiciones son falsas, entonces no
se realizará ninguna acción. El siguiente ejemplo

using System;

class IfElseIf{

public static void Main()

string opcion;
Console.WriteLine("Elija una opción (si/no)");

opcion = Console.ReadLine();

if( opcion=="si" )

Console.WriteLine( "Muy bien, ha elegido si" );

else if( opcion=="no" )

Console.WriteLine( "Ha elegido no" );

else{

Console.WriteLine("No entiendo lo que ha escrito");

le pide al usuario que elija una opción si/no y la procesa usando una
estructura if-else-if. Si la opción no es ni "si" ni "no", entonces se ejecuta la
sentencia else por defecto, que imprime por pantalla el mensaje "No
entiendo lo que ha escrito"

Nota: Hay que tener mucho cuidado que el simbolo = no es igual a ==, el
primero sirve para asignar un valor a una variable y el segundo sirve para
comparar si dos términos son iguales.

[editar]Instrucción switch

La instrucción switch es muy parecida a la estructura if-else-if, sólo que


permite seleccionar entre varias alternativas de una manera más cómoda.
Funciona de la siguiente manera: el valor de una expresión se prueba
sucesivamente con una lista de constantes. Cuando se encuentra una
coincidencia, se ejecuta la secuencia de instrucciones asociada con esa
coincidencia. La forma general de la instrucción switch es la siguiente:
switch( expresión ){

case constante1:

instrucciones;

break;

case constante2:

instrucciones;

break;

...

default:

instrucciones;

break;

La sentencia default se ejecutará sólo si ninguna constante de las que siguen


a case coincide con expresión. Es algo similar al else final de la instrucción if-
else-if.

Sin más, vamos a por un ejemplo

using System;

class InstruccionSwitch{

public static void Main()

string s;

Console.WriteLine( "Elige hacer algo con los números 2 y 3");

Console.WriteLine( " + para sumarlos" );

Console.WriteLine( " - para restarlos" );

Console.WriteLine( " * para multiplicarlos" );


Console.WriteLine( " / para dividirlos (division entera)" );

s = Console.ReadLine();

switch(s){

case "+":

Console.WriteLine("El resultado es {0}", 2+3);

break;

case "-":

Console.WriteLine("El resultado es {0}", 2-3);

break;

case "*":

Console.WriteLine("El resultado es {0}", 2*3);

break;

case "/":

Console.WriteLine("El resultado es {0}", 2/3);

break;

default:

Console.WriteLine("No te entiendo");

break;

El cual solicita al usuario que inserte uno de los símbolos +-*/ , y con un
switch compara los resultados para hacer diferentes acciones dependiendo
del valor de s, que es la cadena de caracteres que almacena la elección del
usuario. El resultado debería ser algo parecido a esto:
Elige hacer algo con los números 2 y 3

+ para sumarlos

- para restarlos

* para multiplicarlos

/ para dividirlos (division entera)

El resultado es 6

Como habrá notado, al final de todo case siempre hay una sentencia break.
Esto no es obligatorio, puede haber en su lugar otra sentencia de salto como
un goto inclusive en el caso default.

Siempre se deberá tener un break o un goto en cada caso a menos que la


sentencia esté vacía. En esta situación se ejecutará el siguiente caso que
viene en la lista. Si no se toma en cuenta ésto se obtiene un error en tiempo
de compilación. Otros lenguajes, como C/C++ o Java no tienen esta
restricción. La razón de adoptarla en C# es doble: por un lado, elimina
muchos errores comunes y en segundo lugar permite al compilador
reorganizar las sentencias de los case, y así permitir su optimización.

Ejemplo:

using System;

class InstruccionSwitch{

public static void Main()

int voto;

Console.WriteLine( "Qué tipo de musica te gusta más");

Console.WriteLine( "1 - Rock" );

Console.WriteLine( "2 - Clásica (clasica cuenta como instrumental)" );

Console.WriteLine( "3 - Instrumental" );

Console.WriteLine( "4 - Alternativa (alternativo cuenta como Rock)" );


voto = Int32.Parse(Console.ReadLine());

switch(voto){

case 1:

Console.WriteLine("Has votado por Rock o Alternativo");

break;

case 2: //Debido a que no tiene ni un goto ni break y está vacía va


al siguiente caso

case 3:

Console.WriteLine("Has votado por Clásica o Instrumental");

break;

case 4:

goto case 1;

default:

Console.WriteLine("No te entiendo");

break;

Como bien se puede notar en el ejemplo, en el case 4, se utiliza la instrucción


goto, indicando que se vaya al case 1, ya que según la lógica del ejemplo, es
igual elegir 1 o 4. Nótese que no es necesario usar break; en el case 4 ya que
se utilizó goto.

[editar]Bucle for

El bucle for de C# es idéntico al encontrado en los lenguajes C/C++ y Java. El


formato general es

for( inicialización; condición; iteración )


{

instrucciones;

Las sentencias de inicialización se ejecutan una vez al principio y sirven


principalmente para asignar valores a las variables que servirán de contador.
Las sentencias de condición, por su parte, se ejecutan cada vez que el bucle
vuelve al principio y sirven para controlar el bucle: éste seguirá realizándose
siempre y cuando estas condiciones sean true. Las sentencias de iteración se
ejecutan también cada vez que se realiza una nuevo ciclo en el bucle, y
sirven para cambiar el estado de las variables que gobiernan las sentencias
de condición. Pero todo esto se entiende mejor con un ejemplo

using System;

class BucleFor{

public static void Main()

int i; //el contador

for( i = 0; i < 10; i++)

Console.WriteLine( i );

Este ejemplo imprime por pantalla los 10 primero enteros positivos. Es un


caso muy simple del bucle for. Por cierto, el operador ++ lo que hace es que
añade una unidad a la variable a la que acompaña, de forma que, por
ejemplo, 9++ es 10. De esta forma, la variable i se incrementa a cada vuelta.

En el ejemplo anterior, las sentencias de inicialización y de iteración eran


únicas, pero esto no tiene por qué ser así, de hecho se pueden utilizar varias
sentencias separadas por comas. Por ejemplo, se pueden usar dos variables
para controlar el bucle

using System;

class BucleFor2{

public static void Main()

int i;

int j;

for( i=0, j=10; i<j; i++, j--)

Console.WriteLine("( {0} , {1} )", i, j);

Por su parte, la expresión condicional del bucle for puede ser cualquier
expresión que genere un valor booleano. En este caso se ha usado "i<j", pero
también hubiera sido válida "i==5", "true" (el bucle se realizará
indefinidamente) o "false" (el bucle no se realizará).

[editar]Bucle while

El bucle while es un bucle que se realiza mientras se cumpla determinada


condición. Tiene la forma

while( condición )

instrucciones;

Donde la condición tiene que ser un valor booleano. Tiene una estructura
muy sencilla, así que vamos a ver directamente un ejemplo.
using System;

class BucleWhile{

public static void Main()

int i = 0;

while( i<10)

Console.WriteLine( i );

i = i+1;

En el que se realiza lo mismo que en el ejemplo anterior, sólo que ahora con
un bucle while.

[editar]Bucle do-while

Se trata de una ligera variante del bucle anterior, con la diferencia de que
ahora primero se ejecutan las instrucciones y luego se evalúa la condición, de
forma que tiene tiene una estructura:

do{

instrucciones;

while( condición );

El siguiente ejemplo

using System;
class BucleDoWhile{

public static void Main()

string s = "";

do

Console.WriteLine( "Introduce si para salir del bucle" );

s = Console.ReadLine();

while( s != "si" );

muestra un programa que ejecuta un bucle hasta que el usuario introduce


"si". Por cierto, != es lo contrario de ==, es decir, != devuelve true cuando
los valores comparados son distintos.

[editar]Bucle foreach

El bucle foreach se utiliza para hacer iteraciones sobre elementos de una


colección, como pueden ser los enteros dentro de un arreglo de enteros. La
sintaxis sigue la siguiente estructura:

foreach( tipo in coleccion )

instrucciones;

Como hemos comentado, el uso más inmediato es iterar sobre un arreglo de


números:

using System;
class BucleForeach{

public static void Main()

int[,] arr = {{1,2},{2,3}};

foreach( int elem in arr )

Console.WriteLine( elem );

Este ejemplo sólo imprime los valores de una matriz, pero como se puede
comprobar mejora mucho la claridad del código comparándolo con una
implementación con bucles for como esta

using System;

class BucleForeach{

public static void Main()

int i, j; //seran los indexadores de la matriz

int[,] arr = {{1,2},{2,3}};

for(i = 0; i<2; i++ )

for( j = 0; j<2; j++ )

{
Console.WriteLine( arr[i,j] );

Además, es posible utilizar el bucle foreach con cualquier tipo que sea una
colección, no solo con arreglos, como veremos más adelante.

[editar]Usando continue y break

continue y break son dos palabras clave que nos permiten saltar
incondicionalmente al inicio de un bucle (continue) o fuera de un bucle
(break) cuando se necesite. Por ejemplo:

using System;

class continueBreak

public static void Main()

for(int i = 0; i<10; i++ )

if (i==5)

continue;

if (i==9)

break;

Console.Write("{0},",i);

}
Este pequeño programa entrará en un bucle for que hará que la variable i
tome los valores del 1 al 10, pero al llegar al número 5 el bucle saltará
incondicionalmente al inicio del bucle sin ejecutar las líneas que siguen más
adelante por lo que no ejecutará la línea que imprime en la pantalla el
número 5. Cosa similar sucede cuando llega al número 9: el bucle será
detenido por el salto incondicional break que romperá el bucle cuando
encuentre esta palabra. El resultado será el siguiente:

0,1,2,3,4,6,7,8,

El bucle saltó la línea que imprime 5 y terminó cuando llegó a 9 gracias a las
palabras clave continue y break.

SEGUNDO TUTORIAL
Versión para imprimir esta pagina

Contenido [ocultar]

1 Introducción a las clases en C#

1.1 Métodos

1.2 Pasando valores a los métodos

1.2.1 Parámetros

1.2.2 Paso por valor

1.2.3 Paso por referencia

1.2.4 Parámetro de salida

1.2.5 Arreglo de parámetros

1.3 Modificadores public y static

1.4 Constructores e instancias de una clase

1.5 Sobrecarga de métodos

1.6 La palabra reservada this


2 Propiedades e indizadores

2.1 Propiedades

2.2 Indexadores

[editar]Introducción a las clases en C#

Como hemos dicho, C# es un lenguaje orientado a objetos. A


diferencia de lenguajes como C++ o Python en los que la
orientación a objetos es opcional, en C# y al igual que en
Java, la orientación a objetos es ineludible, de hecho
cualquier método o variable está contenida dentro de un
objeto. Y el concepto fundamental en torno a la orientación a
objetos es la clase.

Una clase es como una plantilla que describe cómo deben


ser las instancias de dicha clase, de forma que cuando
creamos una instancia, ésta tendrá exactamente los mismos
métodos y variables que los que tiene la clase. Los datos y
métodos contenidos en una clase se llaman miembros de la
clase y se accede a ellos siempre mediante el operador "." .
En el siguiente ejemplo, se definirá una clase, Clase1 y en el
método Main se creará una instancia de Clase1 llamada
MiClase. Una buena idea es jugar un poco con el código para
ver que la instancia de la clase efectivamente tiene los
mismos miembros que la clase Clase1 (que sería la plantilla
de la que hablábamos antes)

using System;

//definimos nuestra clase


class Clase1{

public int a = 1;

private double b = 3;

public char c = 'a';

//usamos la clase que hemos creado

class UsoClase{

public static void Main()

Clase1 MiClase = new Clase1(); // asi creamos una


instancia de Clase1

Console.WriteLine( MiClase.c ); //podemos llamar a


los tipos que hay dentro de Clase1

los identificadores public delante de los tipos que hay dentro


de Clase1 son necesarios para luego poder ser llamados
desde otra clase, como en este caso, que estamos llamando
a los miembros de una instancia de Clase1 desde UsoClase.
Pero en las clases no solo hay variables, también podemos
incluir métodos.

using System;
//definimos nuestra clase

class Clase1{

public int a = 1;

public double b = 3;

public char c = 'a';

public void Descripcion()

Console.WriteLine("Hola, soy una clase");

//usamos la clase que hemos creado

class UsoClase{

public static void Main()

Clase1 MiClase = new Clase1(); // asi creamos una


instancia de Clase1

Console.WriteLine( MiClase.c ); //podemos usar todos


los tipos que hay dentro de Clase1

MiClase.Descripcion();

}
}

Podemos hacer más cosas con las clases, como heredar


otras clases o implementar interfaces, pero en este capítulo
nos centraremos en el uso de métodos y variables.

[editar]Métodos

Los métodos, también llamados funciones, son trozos de


código que reciben unos datos, hacen algo con esos datos, y
a veces devuelven algún valor. En C#, todos los métodos se
encuentran contenidos dentro de un objeto.

La estructura mínima de un método tiene las siguientes


partes:

* Tipo devuelto

* Nombre del método

* Parámetros (puede ser vacío)

* Cuerpo del método

de forma que el siguiente método:

double Divide( double a, double b )

return a/b;

devuelve un tipo double, tiene por nombre Divide, los


parámetos son a y b, ambos del tipo double, y el cuerpo del
método es simplemente "return a/b;".

Cuando queramos llamar a un método, debemos


simplemente poner el nombre del método y sus argumentos
dentro de un paréntesis separados por comas. Para llamar al
método Divide declarado antes, simplemente debemos
escribir Divide(8, 2);

Según lo que hemos visto, el ejemplo del método Divide()


completo necesita tener una clase donde definirse y un
método Main() donde ejecutarse.

using System;

class Metodo{

public double Divide( double a, double b )

return a/b;

class Principal{

public static void Main()

Metodo m = new Metodo();

Console.WriteLine( m.Divide(8, 2) );

[editar]Pasando valores a los métodos

[editar]Parámetros
La declaración formal de parámetros también define
variables. Hay cuatro tipos de parámetros: parámetros por
valor, por referencia, parámetros de salida, y arreglos de
parámetros.

[editar]Paso por valor

El paso de parámetros por valor es usado por defecto para


pasar parámetros a métodos. Cuando se pasa un parámetro
por valor a una función realmente se está pasando una copia
de dicho parámetro, por lo que las modificaciones que le
hagamos al parámetro dentro del método no afectarán al
parámetro original. El ejemplo

using System;

class Test {

static void F(int p) {

p++;

Console.WriteLine("p = {0}", p);

static void Main() {

int a = 1;

Console.WriteLine("pre: a = {0}", a);

F(a);

Console.WriteLine("post: a = {0}", a);

}
muestra un método F que tiene un parámetro por valor
llamado p. El ejemplo produce la salida:

pre: a = 1

p=2

post: a = 1

aunque el valor del parámetro p haya sido modificado dentro


del método, éste parámetro solamente tenía una copia del
valor del parámetro a que pasamos al método; por lo que
cuando imprimimos el parámetro a vemos que éste
parámetro ha mantenido su valor original.

[editar]Paso por referencia

El paso de parámetros por referencia es la contraposición


lógica al paso por valor. En el paso por referencia no se
realiza ninguna copia del objeto, sino que lo que se le pasa a
la función es una referencia del objeto, de forma que el
parámetro pasa directamente a la función y cualquier
modificación sobre el parámetro dentro de la función
afectará al parámetro original

using System;

class Test {

static void Swap(ref int a, ref int b) {

// intercambia los dos valores

int t = a;

a = b;
b = t;

static void Main() {

int x = 1;

int y = 2;

Console.WriteLine("pre: x = {0}, y = {1}", x, y);

Swap(ref x, ref y);

Console.WriteLine("post: x = {0}, y = {1}", x, y);

muestra un método swap que tiene dos parámetros por


referencia. La salida producida es:

pre: x = 1, y = 2

post: x = 2, y = 1

La palabra clave ref debe de ser usada tanto en la


declaración formal de la función como en los usos que se
hace de ésta.

[editar]Parámetro de salida

El parámetro de salida es similar al parámetro por


referencia, salvo que el valor inicial de dicho argumento
carece de importancia. Un argumento de salida se declara
con el modificador out. El ejemplo

using System;
class Test {

static void Divide(int num1, int num2, out int result, out int
resid) {

result = num1 / num2;

resid = num1 % num2;

static void Main() {

int valor1 = 10;

int valor2 = 3;

int respuesta, residuo;

Divide(valor1, valor2, out respuesta, out residuo);

Console.WriteLine("La división de {0} para {1} = {2}


con un residuo de {3}", valor1, valor2, respuesta, residuo);

muestra un método Divide que incluye dos parámetros de


salida. Uno para el resultado (variable result) de la división y
otro para el resto (variable resid). Vemos que estos
resultados son asignados a las variables respuesta y residuo
respectivamente.

[editar]Arreglo de parámetros

Habrá ocasiones que necesitemos pasar varios parámetros a


un método (o función) pero no sabremos con anticipación
cuantos parámetros tendremos que pasar; para esto
podremos usar un arreglo de parámetros. Un arreglo de
parámetros permite guardar una relación de varios a uno:
varios argumentos pueden ser representados por un único
arreglo de parámetros. En otras palabras, los arreglos de
parámetros permiten listas de argumentos de tamaño
variable.

Un arreglo de parámetros se declara con el modificador


params. Sólo puede haber un arreglo de parámetros en cada
método, y siempre debe ser el último parámetro
especificado. El tipo del arreglo de parámetros debe ser
siempre un tipo arreglo unidimensional. Al llamar a la
función se puede pasar uno o varios argumentos del tipo del
arreglo. El ejemplo

using System;

class Test

static void F(params int[] args) {

Console.WriteLine("nº de argumentos: {0}",


args.Length);

for (int i = 0; i < args.Length; i++)

Console.WriteLine("args[{0}] = {1}", i, args[i]);

static void Main() {

F();

F(1);

F(1, 2);
F(1, 2, 3);

F(new int[] {1, 2, 3, 4});

muestra un método F que toma un número variable de


argumentos int, y varias llamadas a este método. La salida
es:

nº de argumentos: 0

nº de argumentos: 1

args[0] = 1

nº de argumentos: 2

args[0] = 1

args[1] = 2

nº de argumentos: 3

args[0] = 1

args[1] = 2

args[2] = 3

nº de argumentos: 4

args[0] = 1

args[1] = 2

args[2] = 3

args[3] = 4
La mayoría de los ejemplos presentes en este capítulo
utilizan el método WriteLine de la clase Console. El
comportamiento para las sustituciones, como muestra el
ejemplo

int a = 1, b = 2;

Console.WriteLine("a = {0}, b = {1}", a, b);

se consigue usando un arreglo de parámetros. El método


WriteLine proporciona varios métodos sobrecargados para el
caso común en el que se pasa un pequeño número de
argumentos, y un método que usa un arreglo de parámetros.

using System;

namespace System

public class Console

public static void WriteLine(string s) {...}

public static void WriteLine(string s, object a) {...}

public static void WriteLine(string s, object a, object b)


{...}

...

public static void WriteLine(string s, params object[]


args) {...}

}
}

[editar]Modificadores public y static

El modificador public lo hemos utilizado anteriormente. Se


puede utilizar en la declaración de cualquier método o
variable, y como es de esperar, produce el efecto de que el
campo afectado se vuelve público, esto es, se puede utilizar
desde otras clases

using System;

class Metodo{

public double Divide( double a, double b )

return a/b;

class Principal{

public static void Main()

Metodo m = new Metodo();

Console.WriteLine( m.Divide(8, 2) );

Si por ejemplo intentamos declarar el método Divide sin el


modificador public, obtendremos un error en tiempo de
compilación. El modificador complementario de public es
private, que provoca que el método o dato solo sea accesible
desde la clase en la que está declarado. Si no se especifica
nada, se toma por defecto el modificador private

De esta forma podríamos separar las clases Metodo y


Principal en dos archivos separados, llamados por ejemplo
metodo.cs y principal.cs . Para compilar esto, bastará
compilar ambos archivos al mismo tiempo, de forma similar
a esto: mcs principal.cs metodo.cs

Además, tampoco es necesario crear una instancia de la


clase sólo para acceder a un método declarado en ella. Para
eso debemos anteponer a la declaración del método el
modificador static. Los métodos estáticos se caracterizan por
no necesitar una instancia de la clase para cumplir su
función, pero como contrapartida, no pueden acceder a
datos propios de la clase.

using System;

class Metodo{

public static double Divide( double a, double b )

return a/b;

class Principal{
public static void Main()

Console.WriteLine( Metodo.Divide(8, 2) );

Los métodos estáticos se utilizan en multitud de situaciones.


Por ejemplo, el método Console.WriteLine() o las funciones
de la librería matemática estándar no son más que métodos
estáticos de sus respectivas clases.

[editar]Constructores e instancias de una clase

Como hemos visto, las instancias de una clase se crean con


la sintaxis

nombreclase objeto = new nombreclase( argumentos


);

donde nombreclase es el nombre que le hemos dado a la


definición de la clase, argumentos es una lista de
argumentos posiblemente vacía y objeto es el nombre que
queremos darle a la instancia de la clase.

Una vez creada una clase, sus miembros se inicializan a sus


valores predeterminados ( cero para valores numéricos,
cadena vacía para el tipo string, etc. ). La siguiente clase
representa un punto sobre el plano, de forma que tiene dos
valores públicos X e Y, y un método que calcula la distancia
al origen del punto (módulo)

using System;
class Punto{

public double X;

public double Y;

public double Modulo()

double d;

d = Math.Sqrt(X*X + Y*Y); //Sqrt = raiz cuadrada

return d;

class Principal{

public static void Main()

Punto A = new Punto();

A.X = 1;

A.Y = 1;

Console.WriteLine("El modulo del punto (1,1) es:


{0}", A.Modulo() );

Ahora bien, la forma en la que se crea la instancia, es decir,


inicializando los datos a cero (ejercicio: comprobar esto), se
puede personalizar, de forma que podemos construir nuestro
propio constructor que le diga a la clase los valores por
defecto que debe tomar. Esto se realiza simplemente
escribiendo dentro de la clase un método que tenga el
mismo nombre que la clase y en el que no se especifica el
valor devuelto. La clase Punto con un constructor sería así:

using System;

class Punto{

public double X;

public double Y;

public Punto() //constructor

X = 1;

Y = 1;

public double Modulo()


{

double d;

d = Math.Sqrt(X*X + Y*Y); //Sqrt = raiz cuadrada

return d;

de forma que ahora al crear una instancia de la clase se crea


el punto (1,1) en lugar del (0,0), que era el que se creaba
por defecto. De esta forma, al crear la instancia, par ya
contendrá los valores (1,1) .

En la práctica se utilizan mucho constructores con


parámetos, de forma que al crear la instancia se le asignan
valores según los parámetros. La siguiente implementación
de Par contiene un constructor que acepta un par de valores,
que servirán para inicializar los valores A y B

class Punto{

public Punto( double val1, double val2)

X = val1;

Y = val2;

...

También tenemos la posibilidad de declarar una clase con


varios constructores (cada uno con diferentes parámetros)
Lo que hará el compilador de C# es buscar el constructor
que se adecúe a los parámetros que le llegan, y ejecutarlo
como si fuera un método más. Dependiendo de la llamada
que se haga en el "new", usaremos un constructor u otro.

[editar]Sobrecarga de métodos

En C#, al igual que en C++ y en Java es posible definir


varios métodos con el mismo nombre pero con distintos
parámetros, de forma que el compilador decide a cuál se
llama dependiendo de los parámetros que le lleguen.

Esto es muy práctico, pues no tienes que renombrar cada


función según el tipo de valor que acepta. El siguiente
ejemplo implementa un par de métodos que elevan al
cuadrado el valor que reciben, y se implementan para tipos
double y para int. En C, que es un lenguaje que no soporta
sobrecarga de métodos, se tendría que haber llamado
distinto a ambos métodos, por ejemplo alcuadrado_double y
alcuadrado_int

using System;

class Eleva{

public static double AlCuadrado( int a )

return a*a;

public static double AlCuadrado( double a )

return a*a;
}

class Principal{

public static void Main()

Console.WriteLine("4 al cuadrado es {0}",


Eleva.AlCuadrado(4) );

Console.WriteLine("3.2 al cuadrado es {0}",


Eleva.AlCuadrado(3.2) );

[editar]La palabra reservada this

La palabra reservada this sirve para hacer referencia a


miembros de la clase en caso de que se quiera especificar,
ya sea por motivos de colisión de nombres o por la claridad
del código. Su sintaxis es

this.campo

donde campo es la variable de la clase a la que queremos


hacer referencia.

En el siguiente ejemplo, declaramos un constructor para la


clase Punto, que toma dos argumentos X e Y. Entonces es
obligado el uso de this para distinguir entre el X de la clase y
el X tomado como parámetro
class Complejo

double X;

double Y;

Complejo(double X, double Y)

this.X = X;

this.Y = Y;

[editar]Propiedades e indizadores

[editar]Propiedades

Las propiedades son una característica de C# que permiten


aparentemente el acceso a un miembro de la clase mientras
mantiene el control asociado al acceso mediante métodos.

Para los programadores de Java hay que decir que esto no es


más que la formalización del patrón de asignación (setter) y
método de lectura (getter)

Las propiedades son como métodos que se declaran dentro


de un bloque asociado a una variable mediante las palabras
reservadas get (se encarga de devolver algo cuando se
llama al tipo que lo contiene ) y set (que hace algo cuando
se le asigna un valor a la variable que lo contiene. Este valor
viene especificado en la variable value )
using System;

class TestProperties {

private static string clave;

public string Clave {

get

Console.WriteLine ("Acceso a la propiedad


clave");

return clave;

set

Console.WriteLine ("Cambio del valor de


clave");

clave = value;

class Test {

public static void Main () {

TestProperties tp = new TestProperties();


string c = "ClaveClave";

tp.Clave = c;

Console.WriteLine (tp.Clave);

En realidad, lo que se hace es declarar una variable privada


de forma que no se puede acceder de forma directa, y se
crean dos métodos ( o uno si solo se requiere acceso de
lectura) que permiten acceder al contenido de la variable y
tal vez modificarla. Si no queremos que se pueda modificar
la variable, no incluímos el método "set" y ya tendríamos
propiedades de sólo lectura.

[editar]Indexadores

Hemos visto, en el apartado en el que tratamos las


propiedades, que podemos acceder a una variable privada
de una clase a través de eventos que nos permiten controlar
la forma en la que accedemos a dicha variable.

Los indexadores nos van a permitir hacer algo parecido. Nos


van a permitir acceder a una clase como si se tratara de un
arreglo. Lo vemos de forma más sencilla con un ejemplo:

using System;

class PruebaIndexadores

private int[] tabla = {1, 2, 3, 4};


public int this [int indice]

get

Console.WriteLine ("La posicion {0} de la tabla tiene el


valor {1}", indice, tabla[indice]);

return tabla[indice];

set

Console.WriteLine ("Escrito el valor {0} en la posición


{1} de la tabla", value, indice);

tabla[indice] = value;

Tenemos una clase PruebaIndexadores en la que hay un


array llamado "tabla", declarado como privado, por lo que no
podremos acceder a él desde fuera de nuestra clase. Pero
hemos declarado también un indexador (public int this [int
indice]), que nos permitirá acceder a él de forma más
controlada.

Para probar esta clase, creamos otra clase con un punto de


entrada (public static void Main ()), que será donde hagamos
las pruebas.

Primero creamos un objeto de la clase PruebaIndexadores:


PruebaIndexadores obj = new PruebaIndexadores ();

Luego accedemos a una posición del indexador:

int a = obj[3];

Esta línea lo que hace es llamar al indexador, pasándole


como parámetro el índice, en este caso 3. Al ser una
consulta de lectura, se ejecuta el código que haya en la
parte "get" del indexador. Una vez ejecutado, lo que nos
aparece por pantalla es esto:

La posicion 3 de la tabla tiene el valor 4

Vamos ahora a hacer un cambio en la tabla:

obj[3] = 6;

Lo que se ejecuta ahora es la parte "set" del indexador. Lo


que aparecerá en pantalla una vez ejecutado esto será:

Escrito el valor 6 en la posición 3 de la tabla


Nótese que tenemos que hacer explícitamente el acceso al
array (tabla[indice]=value) en el set, ya que el indexador no
tiene forma de saber qué variable se supone que tiene que
manejar. Si no pusiéramos esa línea, en realidad el
indexador no cambiaría el valor del array.

Para comprobar que realmente se ha hecho el cambio,


volvemos a acceder al indexador:

a = obj[3];

Y esta vez nos aparecerá esto:

La posicion 3 de la tabla tiene el valor 6.

TERCER TUTORIAL
Herencia

La herencia es un concepto fundamental de la programación orientada


a objetos. Cuando se dice que una cierta clase A hereda otra clase B
significa que la clase A contiene todos los miembros de la clase B más
algunos que opcionalmente puede implementar ella misma

Las clases en C# soportan herencia simple, de forma que una clase


puede derivar de otra, pero no de varias (como si era posible en C++).
De hecho, en C# todas las clases derivan implícitamente de la clase
object.

La sintaxis que se utiliza es la siguiente:

class MiClaseDerivada : MiClaseBase


{

//miembros

En el siguiente ejemplo definimos una clase A con un método F().


Posteriormente definimos una clase B que hereda A y además define
un método G(). Finalmente creamos una clase con un método Main()
que llamará a los dos métodos de B, al implementado por B y al
heredado

using System;

class A{

public void F()

Console.WriteLine("Soy F() de A");

class B : A{

public void G()

Console.WriteLine("Soy G() de B");

}
class Principal{

public static void Main()

B clase_heredada = new B();

clase_heredada.F();

clase_heredada.G();

[editar]La palabra reservada base

La palabra reservada base sirve para acceder a miembros de la clase


heredada de la misma forma que this sirve para acceder a miembros
de la propia clase. Su sintaxis es idéntica a la de this, esto es:

base.nombre_del_miembro

En el siguiente ejemplo declaramos una clase B que hereda A y que


utiliza el método F() de A.

class B : A{

public void H()

base.F();

Console.WriteLine("soy H() de B");

[editar]Clases Abstractas
Las clases abstractas son clases que contienen algún método
incompleto, esto es, que está definido pero no implementado. Por lo
tanto, no se pueden instanciar y su único propósito es servir de clase
base de las que se derivarán otras clases.

Las clases que heredan una clase abstracta deben implementar los
métodos incompletos. Las clases abstractas se declaran con la palabra
reservada abstract

using System;

abstract class A{

public void F(); //metodo no implementado

class B : A{

//error en tiempo de compilación, B tiene que definir un método F()

[editar]Miembros virtual

Métodos, propiedades e indexadores pueden ser virtual, lo que


significa que su implementación puede ser sobreescrita en clases
derivadas. El ejemplo

using System;

class A {

public virtual void F()

Console.WriteLine("A.F");

}
}

class B: A {

public override void F()

base.F();

Console.WriteLine("B.F");

class Test {

public static void Main()

B b = new B();

b.F();

A a = b;

a.F();

muestra una clase A con un método virtual F, y una clase B que


sobreescribe F. El método sobreescrito en B contiene una llamada,
base.F(), el cual llama al método sobreescrito en A.

[editar]Problemas propuestos

Por escribir. Puedes colaborar escribiendo estos problemas.

<<- Anterior (Capítulo 5) | (Capítulo 7) Siguiente->>


Versión para imprimir esta pagina

Este código me pareció interesante de compartir, una modesta


recreación del efecto matrix, escrita en C#.

define readkey

using System;

namespace m7tr1x {

class Program

static void Main(string[] args)

Console.Title = "tH3 M7tr1x 3ff3<t";

Console.ForegroundColor = ConsoleColor.DarkGreen;

Console.WindowLeft = Console.WindowTop = 0;

Console.WindowHeight = Console.BufferHeight =
Console.LargestWindowHeight;

Console.WindowWidth = Console.BufferWidth =
Console.LargestWindowWidth;

if readkey

Console.WriteLine("H1T 7NY K3Y T0 C0NT1NU3 =/");

Console.ReadKey();

endif

Console.CursorVisible = false;

int width, height;

int[] y;

int[] l;

Initialize(out width, out height, out y, out l);


int ms;

while (true)

DateTime t1 = DateTime.Now;

MatrixStep(width, height, y, l);

ms = 10 - (int)((TimeSpan)(DateTime.Now -
t1)).TotalMilliseconds;

if (ms> 0)

System.Threading.Thread.Sleep(ms);

if (Console.KeyAvailable)

if (Console.ReadKey().Key == ConsoleKey.F5)

Initialize(out width, out height, out y, out l);

static bool thistime = false;

private static void MatrixStep(int width, int height, int[] y, int[] l)

int x;

thistime = !thistime;

for (x = 0; x <width; ++x)

if (x % 11 == 10)

if (!thistime)

continue;
Console.ForegroundColor = ConsoleColor.White;

else

Console.ForegroundColor = ConsoleColor.DarkGreen;

Console.SetCursorPosition(x, inBoxY(y[x] - 2 - (l[x] / 40 * 2),


height));

Console.Write(R);

Console.ForegroundColor = ConsoleColor.Green;

Console.SetCursorPosition(x, y[x]);

Console.Write(R);

y[x] = inBoxY(y[x] + 1, height);

Console.SetCursorPosition(x, inBoxY(y[x] - l[x], height));

Console.Write(' ');

private static void Initialize(out int width, out int height, out int[] y,
out int[] l)

int h1;

int h2 = (h1 = (height = Console.WindowHeight) / 2) / 2;

width = Console.WindowWidth - 1;

y = new int[width];

l = new int[width];

int x;
Console.Clear();

for (x = 0; x <width; ++x)

y[x] = r.Next(height);

l[x] = r.Next(h2 * ((x % 11 != 10) ? 2 : 1), h1 * ((x % 11 !=


10) ? 2 : 1));

static Random r = new Random();

static char R

get

int t = r.Next(10);

if (t <= 2)

return (char)('0' + r.Next(10));

else if (t <= 4)

return (char)('a' + r.Next(27));

else if (t <= 6)

return (char)('A' + r.Next(27));

else

return (char)(r.Next(32, 255));

public static int inBoxY(int n, int height)


{

n = n % height;

if (n <0)

return n + height;

else

return n;

CUARTO TUTORIAL
Sobrecarga de operadores

[editar]¿Qué es la sobrecarga de operadores?

La sobrecarga de operadores es la capacidad para transformar los


operadores de un lenguaje como por ejemplo el +, -, etc, cuando se
dice transformar se refiere a que los operandos que entran en juego no
tienen que ser los que admite el lenguaje por defecto. Mediante esta
técnica podemos sumar dos objetos creados por nosotros o un objeto y
un entero, en vez de limitarnos a sumar números enteros o reales, por
ejemplo.

La sobrecarga de operadores ya era posible en c++ y en otros


lenguajes, pero sorprendentemente java no lo incorpora, así que
podemos decir que esta característica es una ventaja de c# respecto a
java, aunque mucha gente, esta posibilidad, no lo considera una
ventaja porque complica el código.

A la hora de hablar de operadores vamos a distinguir entre dos tipos,


los unarios y los binarios. Los unarios son aquellos que solo requieren
un operando, por ejemplo a++, en este caso el operando es 'a' y el
operador '++'. Los operadores binarios son aquellos que necesitan dos
operadores, por ejemplo a+c , ahora el operador es '+' y los operandos
'a' y 'c'. Es importante esta distinción ya que la programación se hará
de forma diferente.

Los operadores que podemos sobrecargar son los unarios, +, -, !, ~, +


+, --; y los binarios +, -, *, /, %, &, |, ^, <<, >>. Es importante decir
que los operadores de comparación, ==, !=, <, >, <=, >=, se pueden
sobrecargar pero con la condición que siempre se sobrecargue el
complementario, es decir, si sobrecargamos el == debemos
sobrecargar el !=.

[editar]Sobrecargando operadores en la práctica

Para mostrar la sobrecarga vamos a usar el repetido ejemplo de los


numeros complejos, ( aunque también valdría el de las coordenadas
cartesianas ). Como se sabe, los números complejos tienen dos partes,
la real y la imaginaria, cuando se suma dos numeros complejos su
resultado es la suma de las dos partes, para ello se va a crear una
clase llamada ComplexNum que contendrá ambas partes. Sin esta
técnica no se podría sumar dos objetos de este tipo con este práctico
método, ya que esta clase no es válida como operando de los
operadores de c#.

Empecemos con el código de la clase de números complejos.

public class ComplexNum

private float img;

private float real;

// constructor de la clase

public ComplexNum(float real, float img)

this.real = real;

this.img = img;
}

// propiedad Real

public float Real{

get{

return real;

set{

real = value;

// propiedad Img

public float Img{

get{

return img;

set{

img = value;

// Sobrescribimos el miembro ToString heredado de Object

override public string ToString()

{
if ( img >= 0 )

return real + "+" + img +"i";

else

return real + "" + img + "i";

public static ComplexNum operator+(ComplexNum a,


ComplexNum b)

return new ComplexNum(a.real + b.real, a.img + b.img);

En el ejemplo hemos puesto la clase, con un constructor , dos


propiedades para obtener los datos privados de la clase y un método
que nos transfoma el número complejo a una cadena de caracteres
para que se pueda visualizar fácilmente. Finalmente hemos
sobrecargado el operador '+', de forma que podremos sumar dos
números complejos como si se tratara de números usuales.

[editar]Operadores binarios

Para empezar vamos a sobrecargar el operador suma('+') para que al


sumar dos objetos de la clase ComplexNum, es decir dos números
complejos obtengamos un número complejo que será la suma de
ambas partes. Cabe destacar que los prototipos para sobrecargar
operadores serán:

public static Operando operator+(Operando a, Operando b)


Este es el prototipo para el operador +, el resto de operadores binarios
van a seguir el mismo patrón. Por tanto el código del método de
sobrecarga será el siguiente:

public static ComplexNum operator+(ComplexNum a,


ComplexNum b)

return new ComplexNum(a.Real + b.Real, a.Img + b.Img);

Este método sobrecarga el operador suma para que podamos sumar


dos números complejos. Un dato a tener en cuenta es que los métodos
que sobrecargan operadores deben ser static. Como se ve en el código
los operandos son 'a' y 'b', que se reciben como parámetro y el
resultado de la operación es otro número complejo que es el que
retorna el método. Por tanto se limita a crear un nuevo número
complejo con ambas partes operadas. De la misma forma podemos
crear la sobrecarga del operador resta('-') para que lleve a cabo la
misma función:

public static ComplexNum operator-(ComplexNum a,


ComplexNum b)

return new ComplexNum(a.Real - b.Real, a.Img - b.Img);

Como vemos el método es idéntico solo que sustituyendo los + por -.


En este caso el trabajo que hacemos dentro del método es trivial pero
podría ser tan complejo como se quisiera.

[editar]Operadores Unarios
En esta sección se verá cómo sobrecargar los operadores unarios, es
decir aquellos que toman un solo operando, como por ejemplo a++. El
prototipo de los métodos que van a sobrecargar operadores unarios
será:

public static Operando operator++(Operando a)

Como antes sustituyendo el ++ por cualquier operador unario. El


ejemplo dentro de nuestra clase de números complejos sería:

public static ComplexNum operator++(ComplexNum a)

float auximg = a.Img;

float auxreal = a.Real;

return new ComplexNum(++auxreal, ++auximg);

A primera vista puede quedar la duda si estamos sobrecargando la


operacion ++a o a++. Este aspecto se encarga el compilador de
resolverlo, es decir, se sobrecarga la operación ++ y el compilador se
encarga de "sumar y asignar" o "asignar y sumar". Este problema no
ocurría en C++, cosa que teníamos que manejar nosotros.

Como hemos dicho antes, la operación que hagamos dentro del


método que sobrecarga el operador es totalmente libre, se puede
poner el ejemplo de multiplicar dos matrices lo que es mas complejo
que sumar dos números complejos.
<<- Anterior (Capítulo 6) | (Capítulo 8) Siguiente->>

Versión para imprimir esta pagina

Categoría: C sharp NET

QUINTO TUTORIAL
Estructuras

La lista de similitudes entre clases y estructuras es larga: las


estructuras pueden implementar interfaces, y pueden tener el mismo
tipo de miembros que las clases. Sin embargo, las estructuras difieren
de las clases en algunos puntos importantes: las estructuras son tipos
por valor en lugar de tipos por referencia, y no permiten la herencia.
Los valores de las estructuras quedan almacenados "en la pila" o
"alineados". Los programadores cuidadosos pueden a veces mejorar el
rendimiento mediante un uso meditado de las estructuras.

Por ejemplo, el uso de una estructura más bien que una clase para un
Punto puede producir una gran diferencia en el número de
asignaciones producidas en memoria en tiempo de ejecución. El
siguiente programa crea e inicializa un arreglo de 100 puntos. Con
Punto implementado como clase, 101 objetos separados son
inicializados ( uno para el vector y uno para cada uno de los 100
elementos )

class Punto

public int x, y;

public Punto(int x, int y) {

this.x = x;

this.y = y;

}
}

class Test

static void Main() {

Punto[] Puntos = new Punto[100];

for (int i = 0; i < 100; i++)

Puntos[i] = new Punto(i, i*i);

Si Punto fuera implementado como una estructura, como en

struct Punto

public int x, y;

public Punto(int x, int y) {

this.x = x;

this.y = y;

únicamente un objeto es inicializado: el elemento del arreglo. Las


instancias de Punto se almacenan de forma alineada en el arreglo. Esta
optimización puede ser mal usada. Usar estructuras en lugar de clases
también puede hacer que una aplicación funcione más lento o utilice
más memoria, pues pasar una instancia de estructura por valor
provoca que una copia de esa estructura sea creada.

[editar]Rendimiento

Como hemos dicho, la principal ventaja por la que se usan en


determinadas circunstancias estructuras y no clases es que en
circunstancias particulares éstas demuestran mucho mejor
rendimiento.

Escrito por: XXX TODO: ejemplo que lo demuestre

SEXTO TUTORIAL
Definición

Una Interfaz es una colección de miembros abstractos relacionados


semánticamente. Una interfaz representa un comportamiento que una
clase dada puede soportar.

El número de miembros de una interfaz dependen del comportamiento


que queramos soportar, por ejemplo todos los objetos que sean
móviles podrían querer soportar los métodos acelerar y frenar.

Según la interfaz de C# una interfaz sería:

public interface IMovil

bool Acelerar(int n);

bool Frenar(int n);

También podríamos declarar dentro de la interfaz una propiedad que


nos permita leer y/o escribir la velocidad que queremos que tome
nuestro objeto.

public interface IMovil


{

bool Velocidad{get; set;}

Dado que una interfaz es una colección de miembros abstractos


cualquier clase o estructura que quiera implementar una interfaz está
obligada a implementar cada uno de los métodos que se declaran en la
interfaz. De esta forma se consigue un cierto tipo de polimorfismo ya
que si varias clases implementan la misma estructura tenemos la
posibilidad de tratar con todas ellas de la misma forma.

Seguramente alguien se preguntara por que usar interfaces pudiendo


usar una clase base abstracta definiendo los métodos anteriores como
abstractos, la primera razón es simplicidad, una clase base abstracta
suele hacer más que definir una colección de métodos, es capaz de
definir métodos públicos, privados, protegidos y también metodos
concretos (estáticos) a los que pueden acceder todas las clases que
deriven de ella mientras que una interfaz se limita a definir una
colección de métodos sin ninguna implementación. La segunda razón
es que C# solamente soporta herencia simple, pero sin embargo
podemos hacer que una clase implemente múltiples interfaces.

He aquí como haríamos para heredar de una clase base e implementar


una interfaz, teniendo en cuenta que VehiculoDeMotor sera nuestra
clase base e IMovil nuestra interfaz.

public class CocheDeportivo : VehiculoDeMotor, IMovil

//Implementación de los métodos abstractos de vehículo

bool Acelerar(int n)

//implementación de Acelerar

bool Frenar(int n)
{

//implementación de Frenar

Hay que tener en cuenta que siempre hay que poner la clase base
antes de las interfaces.

Ahora nuestra clase CocheDeportivo así como cualquier otra clase que
implemente IMovil podra acelerar y frenar, hay que tener en cuenta
que si implementamos IMovil tendremos que implementar
absolutamente todos sus métodos.

[editar]Obteniendo Referencias a la Interfaz

Si hemos creado la clase CocheDeportivo podemos querer saber si


éste soporta el comportamiento de IMovil de modo que podemos hacer
un cast explícito:

CocheDeportivo coche1 = new CocheDeportivo();

IMovil movil = (IMovil) coche1;

movil.Acelerar(30);

En caso de que nuestro objeto implemente la interfaz podríamos


operar sobre él con todos los métodos de la misma, pero en caso de
que no la soporte tendríamos un error en tiempo de ejecución, con lo
cual la forma correcta de hacerlo es:

CocheDeportivo coche1 = new CocheDeportivo();

try{

IMovil movil = (IMovil) coche1;

movil.Acelerar(30);

catch(InvalidCastException e){
//gestión del error

Otra forma de hacerlo sin tener que recurrir a la gestión de


excepciones sería utilizando la palabra reservada as de C#:

CocheDeportivo coche1 = new CocheDeportivo();

IMovil movil;

movil = coche1 as IMovil;

if (movil != null)

movil.Frenar(10);

else

//otro tratamiento

La palabra reservada as pone la variable de tipo interfaz a null si la


interfaz dada no está soportada por el objeto.

Por último también podemos usar la palabra reservada is de C# para


descubrir si un objeto implementa o no una interfaz:

CocheDeportivo coche1 = new CocheDeportivo();

if (coche1 is IMovil)

coche1.Acelerar(10);

else

//otra gestión

[editar]Pasar interfaces como parámetros

Las interfaces son tipos de datos fuertemente tipados (valga la


redundancia) de modo que se pueden pasar como parámetros a
métodos y se pueden usar también como valores de retorno.

Hemos creado la interfaz IGiro de la siguiente manera:

public interface IGiro


{

void GirarDerecha(int grados);

void GirarIzquierda(int grados);

Y queremos que nuestro coche deportivo pueda girar a izquierda y


derecha:

public class CocheDeportivo : VehiculoDeMotor, IMovil, IGiro

//Implementación de los métodos abstractos de vehículo

bool Acelerar(int n)

//Implementación de Acelerar

bool Frenar(int n)

//Implementación de frenar

void GirarDerecha(int grados)

//Implementación de GirarDerecha

void GirarIzquierda(int grados)

//Implementación de GirarIzquierda
}

Como hemos visto para soportar otra interfaz simplemente la


añadimos al final después de una ",".

Ahora supongamos que queremos hacer un método que nos provea de


utilidades para el giro por ejemplo hacer trompos, le podríamos pasar
una interfaz IGiro de la siguiente forma

public class UtilsGiro

public static void Trompo(IGiro giro)

giro.GirarIzquierda(360);

[editar]Implementación Explícita de una Interfaz

Siguiendo con nuestro ejemplo de los coches definimos una nuevas


interfaces:

public interface IAltaVelocidad

void Turbo(bool activar);

//resto de metodods

Como se puede ver nuestra interfaz implementa un método Turbo.


¿Qué pasaría si una clase heredase a su vez de la clase Formula Uno
que también implemente el metodo void Turbo (bool)? Bueno vamos a
verlo:
public class Campeon : Formula1, IAltaVelocidad

public override void Turbo(bool activar)

//gestion del turbo

Esto en un principio sería correcto pero qué pasa si hacemos lo


siguiente:

Campeon miCampeon = new Campeon();

miCampeon.Turbo(true);

IAltaVelocidad iav = (IAltaVelocidad) miCampeon;

iav.Turbo(true);

Ambas veces se llamaría al mismo método, el definido en la clase


Formula1, pero como haríamos si quisiéramos tener dos Turbos
diferentes? la respuesta es hacer que los métodos definidos en la
interfaz sean sólo accesibles desde una referencia a la interfaz, esto es
lo que se llama implementación explícita de una interface.

public class Campeon : Formula1, IAltaVelocidad

public override void Turbo(bool activar)

//gestión del turbo

void IAltaVelocidad.Turbo(bool activar)


{

//gestión del turbo

El segundo método sólo podrá ser llamado si usamos una referencia de


tipo IAltaVelocidad mientras que el primero podrá ser llamado usando
una referencia a Campeon o a Formula1 (su clase base).

Existen algunas reglas extra al hacer esto, por ejemplo no podemos


usar modificadores de accesibilidad (public, private, protected) ya que
si intentamos que sólo se pueda acceder al método desde una
referencia a la interfaz hacerlo sería contraproducente.

También hay que tener en cuenta que pueden haber colisiones de


nombres entre clases base e interfaces y entre interfaces entre si,
técnicamente no existe ninguna diferencia y todas pueden ser tratadas
como hemos explicado arriba.

[editar]Jerarquías de interfaces

Las interfaces pueden servir de base para otras interfaces al igual que
las clases, e igual que en éstas la idea es que vayamos de lo general a
lo particular.

Por ejemplo:

interface IVehiculo

void Acelerar();

void Frenar();

interface IVehiculoGasolina : IVehiculo

void CambiarVelocidadInyeccion(int velocidad);


}

interface IVehiculo4x4: IVehiculoGasolina

void Activar4x4(bool activar);

Al implementar una de estas interfaces en nuestra clase tenemos que


implementar todos los métodos de esta interfaz y de sus ancestros.

public class CocheDeJuguete : IVehiculo

void IVehiculo.Acelerar(int n)

//Gestión del acelerado

void IVehiculo.Frenar(int n)

//Gestión del frenado

public class CocheNormal:IVehiculoGasolina

void IVehiculo.Acelerar(int n)

//Gestión del acelerado

void IVehiculo.Frenar(int n)
{

//Gestión del frenado

void IVehiculoGasolina.CambiarVelocidadInyeccion(int velocidad)

//Gestión de la inyeccion

public class TodoTerreno:IVehiculo4x4

void IVehiculo.Acelerar(int n)

//Gestión del acelerado

void IVehiculo.Frenar(int n)

//Gestión del frenado

void IVehiculoGasolina.CambiarVelocidadInyeccion(int velocidad)

//Gestión de la inyeccion

void IVehiculo4x4.Activar4x4(bool activar)

{
//Gestión de 4x4

Y lógicamente las llamadas a los métodos serían:

TodoTerreno miTodoTerreno = new TodoTerreno();

((IVehiculo4x4)miTodoTerreno).Acelerar(20);

((IVehiculo4x4)miTodoTerreno).Frenar(20);

((IVehiculo4x4)miTodoTerreno).CambiarVelocidadInyeccion(1000);

((IVehiculo4x4)miTodoTerreno).Activar4x4(true);

<<- Anterior (Capítulo 8) | (Capítulo 10) Siguiente->>

Versión para imprimir esta pagina

Categoría: C sharp NET

SEPTIMO TUTORIAL

S-ar putea să vă placă și