Apunte Programación en C#
Apunte Programación en C#
de C#
Apuntes de C# 2
Apuntes de C#
Contenido
Variables y Tipos de Datos ............................................................................................................ 3
Tipos de datos básicos .................................................................................................................. 3
Instrucciones ................................................................................................................................. 4
Concepto de instrucción ........................................................................................................... 4
Instrucciones básicas................................................................................................................. 4
Definiciones de variables locales........................................................................................... 4
Asignaciones .......................................................................................................................... 5
Instrucciones condicionales ...................................................................................................... 5
Instrucción if .......................................................................................................................... 5
Instrucción switch ................................................................................................................. 5
Instrucciones iterativas ............................................................................................................. 6
Instrucción while ................................................................................................................... 6
Instrucción do...while ............................................................................................................ 6
Instrucción for ....................................................................................................................... 7
Instrucción foreach................................................................................................................ 7
Funciones ...................................................................................................................................... 8
Concepto de función ................................................................................................................. 8
Definición de funciones ............................................................................................................. 8
Llamada a funciones.................................................................................................................. 9
Tipos de parámetros. Sintaxis de definición ............................................................................. 9
Parámetros de entrada ......................................................................................................... 9
Parámetros por referencia .................................................................................................. 10
Arrays .......................................................................................................................................... 10
Arrays multidimensionales ...................................................................................................... 12
La clase System.Array .............................................................................................................. 13
Cadenas de texto......................................................................................................................... 13
Registros (Structs) ....................................................................................................................... 15
Listas, Pilas y Colas ...................................................................................................................... 16
Ficheros ....................................................................................................................................... 17
Ficheros de texto ..................................................................................................................... 18
Escritura en ficheros de texto ............................................................................................. 18
Lectura en ficheros de texto ............................................................................................... 19
Ficheros binarios ..................................................................................................................... 19
Escritura en ficheros binarios .............................................................................................. 19
Lectura en ficheros binarios ................................................................................................ 21
Programación Orientada a Objetos............................................................................................. 22
Clases y Objetos ...................................................................................................................... 22
Conceptos fundamentales ...................................................................................................... 22
Control de Acceso a métodos y atributos ............................................................................... 23
Definición de una Clase en C# ................................................................................................. 23
Constructores .......................................................................................................................... 24
Propiedades............................................................................................................................. 25
Variables y Tipos de Datos 3
<tipoVariable> <nombreVariable>;
C# también proporciona una sintaxis más sencilla con la que podremos asignar un valor
a una variable en el mismo momento se define. Para ello se la ha de definir usando esta
otra notación:
Los tipos de datos básicos son ciertos tipos de datos tan comúnmente utilizados en la
escritura de aplicaciones que en C# se ha incluido una sintaxis especial para tratarlos.
System.Int32 a = 2;
Dado lo frecuente que es el uso de este tipo también se ha predefinido en C# el alias int
para el mismo, por lo que la definición de variable anterior queda así de compacta:
int a = 2;
El valor que por defecto se da a los campos de tipos básicos consiste en poner a cero
toda el área de memoria que ocupen. Esto se traduce en que los campos de tipos básicos
numéricos se inicializan por defecto con el valor 0, los de tipo bool lo hacen con false,
los de tipo char con ‘\u0000’, y los de tipo string y object con null.
Instrucciones
Concepto de instrucción
Toda acción que se pueda realizar en el cuerpo de un método, como definir variables
locales, llamar a métodos, asignaciones y muchas cosas más que veremos a lo largo de
este tema, son instrucciones.
Las instrucciones se agrupan formando bloques de instrucciones, que son listas de
instrucciones encerradas entre llaves que se ejecutan una tras otra. Es decir, la sintaxis
que se sigue para definir un bloque de instrucciones es:
{
<listaInstrucciones>
}
Toda variable que se defina dentro de un bloque de instrucciones sólo existirá dentro de
dicho bloque. Tras él será inaccesible y podrá ser destruida por el recolector de basura.
Por ejemplo, este código no es válido:
public void f();
{
{ int b; }
b = 1; // ERROR: b no existe fuera del bloque donde se declaró.
}
Instrucciones básicas
Definiciones de variables locales
Las variables locales son variables que se definen en el cuerpo de los métodos y sólo
son accesibles desde dichos cuerpos. La sintaxis para definirlas es la siguiente:
También pueden definirse varias variables en una misma instrucción separando sus
pares nombre-valor mediante comas. Por ejemplo:
int a=5, b, c=-1;
Instrucciones 5
Asignaciones
Una asignación es simplemente una instrucción mediante la que se indica un valor a
almacenar en un dato. La sintaxis usada para ello es:
<destino> = <origen>;
Instrucciones condicionales
Las instrucciones condicionales son instrucciones que permiten ejecutar bloques de
instrucciones sólo si se da una determinada condición. En los siguientes subapartados de
este epígrafe se describen cuáles son las instrucciones condicionales disponibles en C#
Instrucción if
La instrucción if permite ejecutar ciertas instrucciones sólo si se da una determinada
condición. Su sintaxis de uso es la sintaxis:
if (<condición>)
<instruccionesIf>
else
<instruccionesElse>
Instrucción switch
La instrucción switch permite ejecutar unos u otros bloques de instrucciones según el
valor de una cierta expresión. Su estructura es:
switch (<expresión>)
{
case <valor1>: <bloque1>
break;
case <valor2>: <bloque2>
break;
...
default: <bloqueDefault>
break;
}
Los valores indicados en cada rama del switch han de ser expresiones constantes que
produzcan valores de algún tipo básico entero, de una enumeración, de tipo char o de
tipo string. Además, no puede haber más de una rama con el mismo valor. En realidad,
aunque todas las ramas de un switch son opcionales siempre se ha de incluir al menos
Instrucciones 6
una. Además, la rama default no tiene porqué aparecer la última si se usa, aunque es
recomendable que lo haga para facilitar la legibilidad del código.
Instrucciones iterativas
Las instrucciones iterativas son instrucciones que permiten ejecutar repetidas veces
una instrucción o un bloque de instrucciones mientras se cumpla una condición. Es
decir, permiten definir bucles donde ciertas instrucciones se ejecuten varias veces. A
continuación se describen cuáles son las instrucciones de este tipo incluidas en C#.
Instrucción while
La instrucción while permite ejecutar un bloque de instrucciones mientras se dé una
cierta instrucción. Su sintaxis de uso es:
while (<condición>)
<instrucciones>
Por otro lado, dentro de las <instrucciones> de un while pueden usarse dos instrucciones
especiales:
break;: Indica que se ha de abortar la ejecución del bucle y continuarse
ejecutando por la instrucción siguiente al while.
continue;: Indica que se ha de abortar la ejecución de las <instrucciones> y
reevaluarse la <condición> del bucle, volviéndose a ejecutar la <instrucciones> si
es cierta o pasándose a ejecutar la instrucción siguiente al while si es falsa.
Instrucción do...while
La instrucción do...while es una variante del while que se usa así:
do
<instrucciones>
while(<condición>);
Instrucción for
La instrucción for es una variante de while que permite reducir el código necesario para
escribir los tipos de bucles más comúnmente usados en programación. Su sintaxis es:
for (<inicialización>; <condición>; <modificación>)
<instrucciones>
Al igual que con while, dentro de las <instrucciones> del for también pueden incluirse
instrucciones continue; y break; que puedan alterar el funcionamiento normal del bucle.
Instrucción foreach
La instrucción foreach es una variante del for pensada especialmente para compactar la
escritura de códigos donde se realice algún tratamiento a todos los elementos de una
colección, que suele un uso muy habitual de for en los lenguajes de programación que lo
incluyen. La sintaxis que se sigue a la hora de escribir esta instrucción foreach es:
foreach (<tipoElemento> <elemento> in <colección>)
<instrucciones>
Funciones
Concepto de función
Una función es un conjunto de instrucciones a las que se les da un determinado nombre
de tal manera que sea posible ejecutarlas en cualquier momento sin tenerlas que
reescribir sino usando sólo su nombre. A estas instrucciones se les denomina cuerpo de
la función, y a su ejecución a través de su nombre se le denomina llamada a la función.
La ejecución de las instrucciones de una función puede depender del valor de unas
variables especiales denominadas parámetros de la función, de manera que en función
del valor que se dé a estas variables en cada llamada la ejecución de la función se pueda
realizar de una u otra forma y podrá producir uno u otro valor de retorno.
Definición de funciones
Para definir una función hay que indicar tanto cuáles son las instrucciones que forman
su cuerpo como cuál es el nombre que se le dará, cuál es el tipo de objeto que puede
devolver y cuáles son los parámetros que puede tomar. Esto se indica definiéndolo así:
<tipoRetorno> <nombreFunción>(<parámetros>)
{
<cuerpo>
}
En <tipoRetorno> se indica cuál es el tipo de dato del objeto que la función devuelve, y si
no devuelve ninguno se ha de escribir void en su lugar.
Aunque es posible escribir funciones que no tomen parámetros, si una función los toma
se ha de indicar en <parámetros> cuál es el nombre y tipo de cada uno de ellos,
Funciones 9
separándolos con comas si son más de uno y siguiendo la sintaxis que más adelante se
explica.
Llamada a funciones
La forma en que se puede llamar a una función depende del tipo de función de la que se
trate.
<nombreMétodo>(<valoresParámetros>)
Parámetros de entrada
Un parámetro de entrada recibe una copia del valor que almacenaría una variable del
tipo del objeto que se le pase. Por tanto, si el objeto es de un tipo valor se le pasará una
copia del objeto y cualquier modificación que se haga al parámetro dentro del cuerpo
del la función no afectará al objeto original sino a su copia; mientras que si el objeto es
de un tipo referencia entonces se le pasará una copia de la referencia al mismo y
cualquier modificación que se haga al parámetro dentro de la función también afectará
al objeto original ya que en realidad el parámetro referencia a ese mismo objeto
original. Para definir un parámetro de entrada basta indicar cuál el nombre que se le
desea dar y el cuál es tipo de dato que podrá almacenar. Para ello se sigue la siguiente
sintaxis:
<tipoParámetro> <nombreParámetro>
Por ejemplo, el siguiente código define una función llamada Suma que toma dos
parámetros de entrada de tipo int llamados par1 y par2 y devuelve un int con su suma:
int Suma(int par1, int par2)
{
return par1+par2;
}
Arrays 10
Como se ve, se usa la instrucción return para indicar cuál es el valor que ha de devolver
la función. Este valor es el resultado de ejecutar la expresión par1+par2; es decir, es la
suma de los valores pasados a sus parámetros par1 y par2 al llamarla.
En las llamadas a funciones se expresan los valores que se deseen dar a este tipo de
parámetros indicando simplemente el valor deseado. Por ejemplo, para llamar a la
función anterior con los valores 2 y 5 se haría <objeto>.Suma(2,5), lo que devolvería el
valor 7.
Al llamar a un método que tome parámetros de este tipo también se ha preceder el valor
especificado para estos parámetros del modificador out. Por ejemplo:
Suma(x, ref z)
Arrays
<tipoDatos>[] <nombreArray>;
Por ejemplo, un array que pueda almacenar objetos de tipo int se declara así:
int[] array;
Con esto la array creada no almacenaría ningún objeto, sino que valdría null. Si se desea
que verdaderamente almacene objetos hay que indicar cuál es el número de objetos que
podrá almacenar, lo que puede hacerse usando la siguiente sintaxis al declararla:
<tipoDatos>[] <nombreArray> = new <tipoDatos>[<númeroDatos>];
Por ejemplo, un array que pueda almacenar 100 objetos de tipo int se declara así:
int[] array = new int[100];
Arrays 11
Aunque también sería posible definir el tamaño del array de forma separada a su
declaración de este modo:
int[] array;
array = new int[100];
También podemos crear arrays cuyo tamaño se pueda establecer dinámicamente a partir
del valor de cualquier expresión que produzca un valor de tipo entero. Por ejemplo, para
crear un array cuyo tamaño sea el valor indicado por una variable de tipo int (luego su
valor será de tipo entero) se haría:
int i = 5;
...
int[] arrayDinamico = new int[i];
Hay que tener cuidado a la hora de acceder a los elementos del array ya que si se
especifica una posición superior al número de elementos que pueda almacenar el array
se producirá una excepción de tipo System.OutOfBoundsException.
Para evitar este tipo de excepciones puede consultar el valor del campo de sólo lectura
Length que está asociado a todo array y contiene el número de elementos de la misma.
Por ejemplo, para asignar un 7 al último elemento del array anterior se haría:
array[array.Length – 1] = 7;
// Se resta 1 porque array.Length devuelve 4 pero el último
// elemento de la array es array[3]
Arrays multidimensionales
Un array multidimensional es un array cuyos elementos se encuentran organizando
una estructura de varias dimensiones. Para definir este tipo de arrays se usa una sintaxis
similar a la usada para declarar arrays unidimensionales pero separando las diferentes
dimensiones mediante comas (,). Por ejemplo, un array multidimensional de elementos
de tipo int que conste de 12 elementos puede tener sus elementos distribuidos en dos
dimensiones formando una estructura 3x4 similar a una matriz de la forma:
1 2 3 4
5 6 7 8
9 10 11 12
Incluso puede reducirse aún más la sintaxis necesaria quedando tan sólo:
int[,] arrayMultidimensional;
Aunque los ejemplos de arrays multidimensionales hasta ahora mostrados son de arrays
de dos dimensiones, en general también es posible crear arrays de cualquier número de
dimensiones. Por ejemplo, un array que almacene 24 elementos de tipo int y valor 0 en
una estructura tridimensional 3x4x2 se declararía así:
El acceso a los elementos de un array multidimensional es muy sencillo: sólo hay que
indicar los índices de la posición que ocupe en la estructura multidimensional el
elemento al que se desee acceder. Por ejemplo, para incrementar en una unidad el
elemento que ocupe la posición (1,3,2) del array anterior se haría (se indiza desde 0):
arrayMultidimensional[0,2,1]++;
La clase System.Array
En realidad, todos los arrays que definamos, sea cual sea el tipo de elementos que
contengan, son objetos que derivan de System.Array. Es decir, van a disponer de todos
los miembros que se han definido para esta clase, entre los que son destacables:
Length: Campo de sólo lectura que informa del número total de elementos que
contiene el array. Si el array tiene más de una dimensión o nivel de anidación
indica el número de elementos de todas sus dimensiones y niveles. Por ejemplo:
int[] array = {1,2,3,4};
int[][] array2 = {new int[] {1,2}, new int[] {3,4,5}};
int[,] array3 = {{1,2},{3,4,5,6}};
Console.WriteLine(array.Length); //Imprime 4
Console.WriteLine(array2.Length); //Imprime 5
Console.WriteLine(array3.Length); //Imprime 6
Cadenas de texto
En el primer caso se ha creado un objeto string que representa a la cadena formada por
la secuencia de caracteres José Antonio indicada literalmente (nótese que las comillas
dobles entre las que se encierran los literales de cadena no forman parte del contenido
de la cadena que representan sino que sólo se usan como delimitadores de la misma). En
el segundo caso la variable cadena2 creada se genera a partir de la variable cadena1 ya
existente, por lo que ambas variables apuntarán al mismo objeto en memoria.
Por otro lado, el acceso a las cadenas se hace de manera similar a como si de tablas de
caracteres se tratase: su “campo” Length almacenará el número de caracteres que la
forman y para acceder a sus elementos se utiliza el operador []. Por ejemplo, el siguiente
código muestra por pantalla cada carácter de la cadena Hola en una línea diferente:
public static void Main()
{
string cadena = “Hola”;
Console.WriteLine(cadena[0]);
Console.WriteLine(cadena[1]);
Console.WriteLine(cadena[2]);
Console.WriteLine(cadena[3]);
}
Sin embargo, hay que señalar una diferencia importante respecto a la forma en que se
accede a las tablas: las cadenas son inmutables, lo que significa que no es posible
modificar los caracteres que las forman.
Registros (Structs)
Un registro, que en los lenguajes C y C# se denomina struct, es un tipo de dato
estructurado formado por la unión de varios elementos bajo una misma estructura. Estos
elementos pueden ser, o bien tipos de datos básicos (entero, real, carácter,...), o bien
otras estructuras de datos. A cada uno de esos elementos se le llama campo.
Un registro se diferencia de un array en que éste es una colección de datos iguales, es
decir, todos del mismo tipo, mientras que en una estructura los elementos que la
componen, aunque podrían serlo, no tiene porque ser del mismo tipo.
struct ficha_alumno
{
public string nombre;
public int edad;
public decimal nota;
}
Con esto habríamos definido el tipo de dato ficha_alumno que nos permitirá crear
variables con esa estructura. Para definir variables de ese tipo, simplemente hacemos:
ficha_alumno f;
Listas, Pilas y Colas 16
f.nombre = “Hola”;
f.edad = 23;
f.nota = 5.3M;
Console.WriteLine(f.nombre);
List<<tipoDatos>> <nombreLista>;
Además de definirla, deberemos crearla para poderla usar, lo que haremos con:
Las listas se usan de manera similar a los arrays, con unas cuantas diferencias:
Las listas no tienen un tamaño definido, por lo que a la hora de crearlas no hay
que especificar el tamaño.
Para añadir un nuevo dato a la lista, usaremos la función Add(), que nos añade
un elemento en la última posición. Así, iremos añadiendo todos los valores que
necesitemos.
lista.Add(25);
Para saber cuántos elementos hay en una lista, usaremos la función Count. En
un array usábamos Length que nos devolvía el tamaño que habíamos
reservado.
Ficheros 17
Console.WriteLine(lista.Count);
Salvo estas diferencias, una lista se puede usar básicamente de la misma forma que se
usa un array. No obstante, las listas disponen de funciones específicas que nos
permiten realizar las operaciones más comunes. A continuación, se enumeran las más
importantes:
int IndexOf(<tipo dato> valor): Nos devuelve la posición del elemento, si existe.
Si el elemento no existe, nos devolverá -1.
bool Exists(<tipo dato> valor): Nos devuelve true si el valor existe dentro de la
lista y false en caso contrario.
void Clear(): Elimina todos los elementos de la lista y nos la deja preparada para
volver a introducir datos.
bool Remove(<tipo dato> valor): Elimina la primera vez que aparece el valor
dentro de la lista. Nos devolverá true si ha encontrado y borrado el valor y false
si el valor no se ha encontrado dentro de la lista.
int RemoveAll(<tipo dato> valor): Elimina todas las veces que aparece el valor
dentro de la lista. Nos devolverá un entero que se corresponderá al número de
elementos que han sido eliminados.
Ficheros
Ficheros 18
Los ficheros nos permiten guardar y recuperar información desde los dispositivos de
almacenamiento. Básicamente, hay dos tipos de ficheros: ficheros de texto y ficheros
binarios. Ambos funcionan de manera similar, pero en los ficheros de texto sólo
podemos guardar cadenas de caracteres mientras que en los ficheros binarios podemos
guardar todo tipo de datos. En C#, usaremos unas herramientas denominada streams
que nos permitirán acceder de manera cómoda a la información que se encuentra
almacenada en los ficheros.
El proceso para utilizar un archivo consta de tres pasos: apertura del fichero, lectura y
escritura de datos, y cerrado del fichero. En C#, utilizaremos diferentes instrucciones
para los ficheros de texto y los ficheros binarios, y también diferentes opciones según
queramos leer o escribir en el fichero.
Ficheros de texto
Una vez abierto, podremos escribir texto en él. La función a utilizar será Write si
queremos escribir sin que salte a la línea siguiente y WriteLine si queremos que nos
salte (lo más usual).
sw.WriteLine(“Hola”);
Cuando hayamos terminado de escribir todo el fichero, deberemos cerrarlo para que se
guarden completamente los datos en el disco.
sw.Close();
Ficheros 19
Para leer texto desde un fichero, primero abriremos el fichero en modo lectura:
Para leer datos del fichero, normalmente lo haremos por líneas mediante la función
ReadLine.
while(!sr.EndofStream)
{
string cadena = sr.ReadLine();
...
}
Ficheros binarios
Al contrario que en los ficheros de texto, los ficheros binarios disponen de muchas
opciones de apertura, dependiendo de que queramos crear un fichero nuevo,
modificarlo, añadir datos, etc. Las diferentes opciones son:
Ficheros 20
Las opciones que usaremos normalmente serán Create para escribir datos en un fichero
(o Append si queremos añadir más datos) y Open para leer datos del fichero.
Una vez abierto, podremos escribir datos en él. Sólo utilizaremos una función para
escribir datos, que es la orden Write. Esta función tiene varias versiones que se
seleccionan automáticamente dependiendo del tipo de dato que le pasemos. Así,
podremos usar la orden Write para escribir un entero, un real, una cadena, etc.
int n = 100;
bw.Write(n);
Cuando hayamos terminado de escribir todo el fichero, deberemos cerrarlo para que se
guarden completamente los datos en el disco.
bw.Close();
Para leer datos desde un fichero binario, primero abriremos el fichero en modo lectura:
Como ya hemos mencionado, el modo más normal para leer ficheros es Open.
Leer datos desde un fichero binario es mucho más complicado que escribirlos o que
hacerlo en un fichero de texto. Para leer un dato desde un fichero binario, tendremos
que conocer el tipo del dato y usar la función correspondiente para cada tipo de dato.
Si conocemos el número de datos que contiene el fichero, podemos hacer un bucle for
para irlos leyendo, pero si no lo conocemos, tendremos que usar una función especial
que nos avisa cuando hemos llegado al final del fichero. Para ficheros binarios es ditinta
que para ficheros de texto. En este caso se llama PeekChar y se usa de la siguiente
forma:
Programación Orientada a Objetos 22
while(br.PeekChar() != -1)
{
int n = br.ReadInt32();
...
}
Clases y Objetos
Una clase es la definición de las características concretas de un determinado tipo de
objetos. Es decir, de cuáles son los datos y los métodos de los que van a disponer todos
los objetos de ese tipo. Por esta razón, se suele decir que el tipo de dato de un objeto es
la clase que define las características del mismo.
Conceptos fundamentales
La programación orientada a objetos introduce nuevos conceptos, que superan y
amplían conceptos antiguos ya conocidos. Entre ellos destacan los siguientes:
Programación Orientada a Objetos 23
<miembros>
}
Por ejemplo, si queremos definir un atributo privado de tipo entero y otro público de
tipo cadena, escribiremos:
Por ejemplo:
objeto.cadena = “hola”;
objeto.InsertaValor(3);
Constructores
Los constructores inicializan el estado de un objeto a la hora de crearlo. Se definen de
manera similar a los métodos, sólo que no devuelven ningún valor (en teoría devuelven
un objeto de la misma clase, pero no hace falta indicarlo ni devolverlo con un return) y
tienen que tener el mismo nombre que la clase. Así, para definir un constructor para la
clase “prueba”, escribiremos:
Los constructores se utilizan, generalmente, para darle valores iniciales a los atributos
del objeto. Al menos se debe crear un constructor por defecto (sin parámetros).
Propiedades
Las propiedades nos permiten acceder de manera controlada a los atributos de una clase.
Para ello, cuentan con dos métodos especiales, denominados set y get que hacen la
función de escribir un valor y leer un valor, respectivamente. Una propiedad se puede
definir como de sólo-lectura si sólo escribimos la cláusula get.
La sintaxis usada para definir una propiedad es la siguiente:
set
{
<instrucciones>
}
}
El tipo será normalmente el mismo que el del atributo, aunque a veces podemos hacer
una propiedad de tipo cadena, por ejemplo, para un atributo de tipo entero que nos
permite representar el valor de otra forma.
En la cláusula get, tendremos que devolver el valor adecuado usando return, de manera
similar a como lo haríamos en una función.