You are on page 1of 61

Introducción a los Lenguajes de Programación

Comencemos con lo más elemental, cuando dos o mas personas se comunican, para
transmitirse información, expresar ideas, noticias, conceptos y hasta contarse algún
chisme, utilizan un elemento para hacerlo y a tal elemento lo conocemos como
Lenguaje. Sin ahondar en formalidades, un lenguaje es una herramienta para
comunicarse por medio de signos, que pueden ser, escritos, sonoros, gestuales, etc.
Usualmente, al lenguaje que usamos los humanos (independientemente del idioma) se le
llama Lenguaje Natural. De esta manera, por ejemplo, un español, un mexicano y un
uruguayo se comunicarán entre sí usando su lenguaje natural (que en particular es
expresado en Español con sus variantes regionales), de forma análoga dialogarían un
australiano, un irlandés y un estadounidense (seguramente comunicándose en Inglés con
sus variantes regionales). La importancia en si, de ésta herramienta, es el poder de
comunicar, solicitar e incluso dar instrucciones para realizar algo concreto. En el mundo
de la informática, debemos ser muy precisos en como damos esas indicaciones y por
ello se procura siempre que todo sea claro, sin confusiones, contradicciones ni
ambigüedades. El Lenguaje Natural humano, es complejo para ser interpretado por
máquinas, para ello, a lo largo de la historia, se han creado lenguajes simples y
concretos, para poder interactuar con máquinas. La Programación, es una actividad en la
cual tenemos que dar instrucciones precisas a una máquina, para que haga exactamente
lo que queremos y para ello, se han creado y perfeccionado lenguajes, conocidos hoy
como Lenguajes de Programación.

Los actuales lenguajes de programación tienen características que pueden asemejarse


en buen grado a los lenguajes naturales como el Español, Inglés, o cualquier otro; ya
que al igual que en la comunicación oral o escrita, los lenguajes de programación tienen
un vocabulario definido, además de reglas ortográficas, de estructura, de puntuación, es
decir la sintaxis que deben seguir y por supesto una semántica, esto es el significado que
adquieren las conformaciones basadas en las reglas antes mencionadas.
Como en los lenguajes humanos corrientes (Lenguajes Naturales), estas reglas deben
tomarse en cuenta para que las ideas que tratamos de expresar tengan sentido. También
sucede que un lenguaje de programación cambia con el tiempo, igual que un lenguaje
natural . Si tú eres un hispano-hablante sabrás que en cada país hispanoamericano se
usan palabras que en otros países no se utilizan o las mismas palabras tienen un
significado distinto entre países (esto es llamado ambigüedad): el idioma Español ha
venido evolucionando con el tiempo y se ha adaptado a la geografía.
En este mismo sentido, el lenguaje de programación C#.NET es una actualización
técnica de otros lenguajes de programación más antiguos como lo son el C, C++, Java,
Visual Basic, Delphi, etc. El creador de este lenguaje Anders Heljsberg, es un
calificado profesional de la informática que además ha desarrollado lenguajes y
herramientas como Turbo Pascal, Delphi (de Borland Co.) y Visual J++ (Microsoft).
Por lo que C# pretende ser el siguiente eslabón en la evolución de los lenguajes de
programación mas avanzados, buscando combinar en proporciones adecuadas: Potencia,
Sencillez y Actualidad/Tendencia.

La evolución de los ordenadores (computadores) y de los lenguajes de programación ha


sido grandísima. No hace muchos años existían personas que tenían que hacer agujeros
en tarjetas de cartulina para poder dar instrucciones al computador: eran las famosas
tarjetas perforadas. Pocos años despúes de esos días existían personas (y hasta ahora
existen aquellos) que pueden "hablar con el ordenador", se trata de informáticos que en
su época solo disponían de elementos de muy bajo nivel para controlar el computador,
eso hasta el día de hoy es la base de las técnicas de cómputo, independientemente de
cuanto hayan aumentado las capacidades de memorias, velocidades de
microprocesadores, miniaturizaciones, etc.
La forma en que una máquina de cómputo recibe instrucciones es una manera peculiar.
Nos la idea aquí ahondar sobre este tema, pero es importante brindar una idea al
respecto.
Supóngase que se cuenta con un sistema de riego, en principio asúmase algo simple, un
jardín con un dispersor de agua, que se activa o desactiva utilizando una simple llave de
encendido y apagado, como las de las luces.
De esta forma podemos decir que tenemos un sistema que interpreta o entiende 2
instrucciones:
Llave para arriba ---> ENCENDIDO Llave hacia abajo ---> APAGADO
De esta manera el sistema se enciende o se apaga conforme alguien le da una de estas 2
instrucciones que el mismo interpreta o "entiende".
Podemos esquematizar el referido sistema de esta forma:
[ INSTRUCCIONES ]====>[ PROCESAMIENTO ]====>[ Acciones / Resultados ]
Para tratar de simplificar la forma de transmitir o escribir una de esas instrucciones,
podemos decir que si al bloque de procesamiento le enviamos la instrucción "1",
implica un ENCENDIDO, es decir que le estamos pidiendo poner en marcha el sistema.
Por el contrario, si le enviamos un "0", es un APAGADO, le estamos pidiendo detener
el sistema. Hemos hecho esto, porque como antes mencionamos, las máquinas deben
trabajar con interpretaciones lo mas concreta y simplificadas posibles. Es mas fácil para
una máquina elemental, interpretar un "1", un "0" o incluso una serie de éstos números
[*].
Así que ahora tenemos algo como un lenguaje elemental para dar indicaciones, donde
tenemos dos instrucciones:
Instrucción: ENCENDIDO ............. Equivale a: 1
Instrucción: APAGADO ................ Equivale a: 0
Imaginemos ahora que el sistema de riego, además de tener encendido y apagado, tiene
otra modalidad llamada DIARIO, que simplemente provoca que el mismo se encienda
una vez al día, a una hora indicada, por un lapso determinado (ej: 90 minutos).
Así que necesitamos otra representación o equivalencia simple para indicar esta nueva
instrucción. Reformulemos el caso anterior, de la siguiente manera:
Instrucción: ENCENDIDO .............. Equivale a: 01
Instrucción: APAGADO ................ Equivale a: 00
Instrucción: DIARIO ................. Equivale a: 10

o

 Añadir más para ilustrar sobre el lenguaje binario (bajo
nivel) y los lenguajes de alto nivel**** (en construcción)
Ya que hemos recalcado las similitudes entre los lenguajes de programación y los
lenguajes naturales también hay que tener presente que un lenguaje de programación es
mucho más estricto que uno natural en el sentido de que no pueden existir
ambigüedades. Es muy fácil en el idioma Español tener frases que impliquen algo que
ya conocemos o que signifiquen cosas diferentes. Por el contrario los lenguajes de
programación son claros, específicos y directos, no se admiten ambigüedades ni
suposiciones.
Para poder dominar un lenguaje de programación (al igual que con los lenguajes
naturales) se requiere mucho estudio pero por sobre todo muchísima práctica. Una
persona podría leer este manual unas 500 veces pero si no pone en práctica los
conceptos ni investiga por su cuenta, jamás llegará a dominar este maravilloso lenguaje
de programación. Así que: ¡a practicar se ha dicho!

Capítulo 1
Como hemos dicho C# (C Sharp) es parte de la plataforma .NET. C# es un lenguaje
orientado a objetos simple, seguro, moderno, de alto rendimiento y con especial énfasis
en internet y sus estándares (como XML). Es también la principal herramienta para
programar en la plataforma .NET.
Tal vez os habréis preguntado ¿Qué es la plataforma .NET? ¿Porqué Microsoft está
invirtiendo tanto en esta nueva tecnología? ¿Qué es lo que es tan novedoso? ¿Como es
que con .NET se pueden producir aplicaciones multi-plataforma? A continuación
hablaremos un poco de la plataforma .NET
La plataforma .NET

Marco de trabajo .NET


La plataforma .NET es una plataforma de desarrollo de software con especial énfasis en
el desarrollo rápido de aplicaciones, la independencia de lenguaje y la transparencia a
través de redes.
La plataforma consta de las siguientes partes:
• Un conjunto de lenguajes de programación (C#, J#, JScript, C++ gestionado,
Visual Básic.NET, y otros proyectos independientes).
• Un conjunto de herramientas de desarrollo (entre ellos Monodevelop o Visual
Studio.NET de Microsoft )
• Una libreria de clases amplia y común para todos los lenguajes.
• Un sistema de ejecucion de Lenguaje Común. (CLR).
• Un conjunto de servidores .NET
• Un conjunto de servicios .NET
• Dispositivos electrónicos con soporte .NET (PDA,Celulares, etc).
Los puntos fuertes de la plataforma son:

Independencia de lenguaje
Todos los lenguajes que conformen con los estándares .NET, sin importar cual, podrán
interoperar entre sí de forma totalmente transparente, las clases podrán ser heredadas
entre unos lenguajes y otros, y se podrá disfrutar de polimorfismo entre lenguajes. Por
ejemplo, si yo tengo una clase en C#, esta clase podrá ser heredada y utilizada en Visual
Basic o JScript o cualquier lenguaje .NET. Todo esto es posible por medio de una de las
características de .NET llamado Common Type System (CTS). También tiene la
cualidad de que se pueden incluir más lenguajes a la plataforma. En la actualidad
existen proyectos independientes de incluir PHP, Python, Ada y otros lenguajes en la
plataforma.

Librería de clases común


Más de 4000 clases, objetos y métodos incluidos en la plataforma .NET están
disponibles para todos los lenguajes.

Multiplataforma
Cuando un programa es compilado, no es compilado en un archivo ejecutable sino en un
lenguaje intermedio llamado “Lenguaje Intermedio” (IL) el cual podrá ser ejecutado por
el CLR (Common Language Runtime) en la plataforma en que el CLR esté disponible
(hasta el día de hoy Microsoft solamente tiene un CLR para los sistemas operativos
Windows, pero el proyecto Mono (www.mono-project.com) y dotGNU
(www.dotGNU.org) han puesto a disposición un CLR para GNU/Linux, MacOS y
muchas otras plataformas). Los sistemas operativos Windows XP o superiores incluyen
el CLR nativamente y SuSE Linux 9.3 o superior planea incorporar el CLR (Mono) en
su distribución lo que quiere decir que un programa .NET podrá ser compilado y
ejecutado en cualquiera de estas plataformas, o en cualquier plataforma que incluya un
CLR.
El CLR compilará estos archivos IL nuevamente en código de máquina en un proceso
que se conoce como JIT (justo a tiempo) el cual se ejecutará cuando se requiera. Este
proceso producirá código de máquina bien eficiente que se reutilizará si es que hubiera
código que se repitiera, haciendo que los programas sean ejecutados muy
eficientemente.
El CRL

Windows Forms, Web Forms, Web Services


La plataforma .NET incluye un conjunto de clases especial para datos y XML que son
la base de 3 tecnologías claves: Servicios Web (Web Services), Web Forms, y Windows
Forms los cuales son poderosas herramientas para la creación de aplicaciones tanto para
la plataforma como para el Web.

Estandarización
Además de los méritos técnicos, una de las razones del éxito de la plataforma .NET ha
sido por el proceso de estandarización que Microsoft ha seguido (y que ha sorprendido a
más de uno). Microsoft, en lugar de reservarse todos los derechos sobre el lenguaje y la
plataforma, ha publicado las especificaciones del lenguaje y de la plataforma, que han
sido posteriormente revisadas y ratificadas por la Asociación Europea de Fabricantes de
Computadoras (ECMA). Esta especificación (que se puede descargar libremente de
Internet) permite la implementación del lenguaje C# y de la plataforma .NET por
terceros, incluso en entornos distintos de Windows. Mono Hispano mantiene una
traducción del estándar que describe el lenguaje C# en http://monohispano.org/ecma/
(Enlace roto)

Un resúmen introductorio sobre el lenguaje C#


El lenguaje es muy sencillo, sigue el mismo patrón de los lenguajes de programación
modernos. Incluye un amplio soporte de estructuras, componentes, programación
orientada a objetos, manipulación de errores, recolección de basura, etc, que es
construido sobre los principios de C++ y Java. Como sabréis, las clases son la base de
los lenguajes de programación orientados a objetos, lo cual permite extender el lenguaje
a un mejor modelo para solucionar problemas. C# contiene las herramientas para definir
nuevas clases, sus métodos y propiedades, al igual que la sencilla habilidad para
implementar encapsulación, herencia y polimorfismo, que son los tres pilares de la
programación orientada a objetos. C# tiene un nuevo estilo de documentación XML que
se incorpora a lo largo de la aplicación, lo que simplifica la documentación en línea de
clases y métodos. C# soporta también interfaces, una forma de estipular los servicios
requeridos de una clase. Las clases en C# pueden heredar de un padre pero puede
implementar varias interfaces. C# también provee soporte para estructuras, un concepto
el cual ha cambiado signifivamente desde C++. Una estructura es un tipo restringido
que no exige tanto del sistema operativo como una clase. Una estructura no puede
heredar ni dar herencias de clases pero puede implementar una interfaz. C# provee
características de componentes orientados, como propiedades, eventos y construcciones
declaradas (también llamados atributos). La programación orientada a componentes es
soportada por el CLR. C# provee soporte para acceder directamente a la memoria
usando el estilo de punteros de C++ y mucho más.

C# frente a Java
C# y Java son lenguajes similares, de sintaxis basada en C/C++, orientados a objetos, y
ambos incluyen las características más importantes de los lenguajes modernos, como
son la gestión automática de memoria y la compilación a código intermedio. Pero por
supuesto, también hay diferencias.
Una de las diferencias más importantes es que C# es mucho más cercano a C++ en
cuanto a diseño se refiere. C# toma casi todos sus operadores, palabras reservadas y
expresiones directamente de C++. También se han mantenido algunas características
que en Java se han desestimado. Por ejemplo, la posibilidad de trabajar directamente
con direcciones de memoria. Si bien tanto Java como .NET proporcionan gestión
automática de memoria, en C# es posible usar lo que se denomina "código no seguro".
Cuando se usa código no seguro en C# es posible operar con punteros de forma muy
similar a como se haría en C/C++, pero el código que utiliza punteros se queda marcado
como no seguro y no se ejecuta en entornos en los que no tiene permisos.

C# frente a C++
Puesto que C# se ejecuta en una máquina virtual, ésta se hace cargo de la gestión de
memoria y por lo tanto el uso de punteros es mucho menos importante en C# que en C+
+. C# también es mucho más orientado a objetos, hasta el punto de que todos los tipos
usados derivan en última instancia el tipo 'object'. Además, muchos tipos se usan de
forma distinta. Por ejemplo, en C# se comprueban los límites de los arrays antes de
usarlos, evitando así que se pueda escribir pasado el final del vector.
Al igual que Java, C# renuncia a la idea de herencia múltiple de clases presente en C++.
Sin embargo, referido a clases, C# implementa 'propiedades' del tipo de las que existen
en Visual Basic, y los métodos de las clases son accedidos mediante '.' en lugar de '::'.

¿Por qué C#?


La plataforma .NET acepta varios lenguajes. Por ahora, C#, Visual Basic, C++
gestionado, Nemerle, FORTRAN, Java, Python, etc. , y con capacidad para aceptar
prácticamente cualquier lenguaje. Entonces la pregunta es, ¿porqué se eligió C# en lugar
de cualquier otro lenguaje?.
La razón fundamental es que C# se diseñó para la plataforma .NET y es capaz de
utilizar todo su potencial. También es cierto que es un lenguaje "limpio" en el sentido de
que al no tener que proporcionar compatibilidad hacia atrás se ha tenido más libertad en
el diseño y se ha puesto especial hincapié en la simplicidad. Por ejemplo, en C# hay un
tipo de clase y siempre se le aplica el recolector de basura mientras que en C++
gestionado hay dos tipos de clases, una a la que se aplica el recolector y otra a la que no.

Instalando lo necesario para empezar


Para poder empezar con nuestro curso debéis tener instalado en vuestro ordenador los
archivos básicos para poder compilar y ejecutar vuestros programas. El conjunto de
utilidades "Microsoft .NET Framework" y el ".NET Framework SDK" para Windows y
el proyecto MONO o dotGNU para Linux, MacOS, BeOS proporcionan estas
herramientas. Podréis encontrarlas en las siguientes direcciónes:

CAPÍTULO 2

Primer programa ¡Hola Mundo!


En un alarde de originalidad, empezaremos con un simple programa que desplegará la
frase “¡Hola Mundo!”. Con este programa introduciremos las bases de nuestro estudio.
Debido a que la plataforma .NET es independiente del sistema operativo en este libro
procuraremos mantenernos imparciales en cuanto este asunto, eso si, cuando llegue el
tiempo de compilar y ejecutar daremos ejemplos de como hacerlo tanto en Windows
como en Linux (en otras plataformas como MacOS será lo mismo que en Linux).
Si estáis utilizando un entorno de desarrollo integrado debéis abrir un nuevo proyecto de
consola en C#, escribir o copiar lo que se muestra en el ejemplo 2.1 y compilad el
proyecto para ver su resultado. Si preferís programar con un editor de textos, abrid
vuestro editor de texto favorito y grabad en un directorio conocido lo que se encuentra
en el ejemplo 2.1 como Programa.cs, abrid una ventana de terminal (en linux) o una
ventana de comandos (Ejecutar(Run) -> cmd) en Windows y compilad vuestro proyecto
con el siguiente comando:
en linux con MONO(en el directorio donde habéis grabado vuestro programa):
mcs Programa.cs
en linux con dotGNU(en el directorio donde habéis grabado vuestro programa):
cscc -o Programa.exe Programa.cs
en Windows (en el directorio donde habéis grabado vuestro programa):
csc Programa.cs
de esta forma obtendréis vuestro primer programa. Para ejecutarlo simplemente
escribid:
con MONO:
mono Programa.exe
con dotGNU:
ilsrun Programa.exe
o en Windows:
Programa

Ejemplo 2.1 Programa Hola Mundo


//Ejemplo 2.1 - Programa Hola Mundo
class HolaMundo
{
static void Main()
{
string var="Mundo";
System.Console.WriteLine ("Hola {0}!", var);
}
}

Analicemos paso a paso nuestro programa de ejemplo:

Comentarios
En la primera línea de nuestro programa encontramos lo siguiente: //Ejemplo 2.1 ....
Esta línea es un ejemplo de comentarios. Un comentario es una parte en vuestro
programa que será ignorado por el compilador.
Existen tres tipos de comentarios en C#. El primer tipo es comentario de un sola línea.
El segundo es comentario de varias líneas. El tercer tipo es para crear documentación.
Este último tipo de comentario lo estudiaremos en el capítulo 13 de este libro.
Los comentarios de una sola línea pueden ser incluidos en líneas independientes, en
líneas que ya incluyen código para comentar sobre lo que hace esa línea o para
comentar una línea de código que no necesitamos.
Los comentarios de varias líneas sirven para incluir muchas líneas de texto o código
como un comentario. Tienen una marca de inicio que dice cuando empieza el
comentario y una marca que indica el final de dicho comentario. La marca de inicio es
/* y la marca para finalizar es */ Así por ejemplo tenemos:
//Este es un ejemplo de comentarios de una línea

/*
este comentario
abarca varias lineas
*/

class HolaMundo
{
static void Main()
{
string var="Mundo";
System.Console.WriteLine ("Hola {0}!", var); //Este comentario puede describir lo
que esta función hace
//System.Console.WriteLine ("y esta linea no la vamos a desplegar");
}
}

Clases, Objetos y tipos


La esencia de la programación orientada a objetos es la creación de nuevos tipos. Un
tipo es la representación de un componente (Pensado en un ordenador (un computador)
que está compuesto de varios componentes). En programación un componente es algo
que cumple una función. Por ejemplo, un componente puede ser un botón en un
programa. En una aplicación podemos tener varios botones del mismo tipo como por
ejemplo botones de Aceptar, Cancelar, etc. Porque son del mismo tipo estos botones
tienen propiedades y comportamientos similares. Pueden tener propiedades como
cambiar de "tamaño", "posición", etc. Las propiedades son las mismas pero los valores
almacenados en sus atributos pueden ser diferentes. Por ejemplo, un botón puede tener
tamaño 10 y otro tamaño 6. Podemos decir entonces que tenemos varias instancias del
mismo componente (o varios botones del mismo tipo) con diferentes valores en sus
atributos.
La programación orientada a objetos es esto. Crear componentes que puedan ser
reusados. Así no tenemos que programar varios componentes que cumplan funciones
similares sino solo uno que podrá llevar diferentes atributos y que podrá ser reproducido
(o instanciado) tantas veces como lo necesitemos.
Como en varios lenguajes de programación orientado a objetos, en C# los tipos o
componentes son definidos por una clase (class en inglés). Las diferentes
reproducciones o instancias de un componente del mismo tipo se conoce como objetos.
Posteriormente en nuestro estudio veremos que en C# hay más tipos además de las
clases. Aprenderemos sobre enums, structs y delegates. Pero por el momento nos
dedicaremos a las clases.
Como hemos visto en nuestro primer programa (Ejemplo 2.1), empezamos nuestro
programa declarando el tipo de componente que vamos a utilizar (un componente de
tipo class) y su nombre HolaMundo. Para declarar nuestro componente como una clase,
primero introducimos la palabra clave class, y después el nombre de nuestro
componente, después de lo cual declaramos las funciones que va a cumplir su
comportamiento y sus propiedades. Todas las funciones, comportamiento y propiedades
de nuestro componente (en este caso nuestra clase) deberán estar encerrados entre llaves
{ }. Las llaves delimitan el inicio y el final de dicho componente.

Métodos o Funciones
Los métodos o funciones son trozos de código que realizan una acción, esto es, toman
unos argumentos y devuelven un valor. En C#, las funciones deben ser declaradas
dentro de un objeto, normalmente dentro de una clase.
Las funciones normalmente llevan nombre que definen su función. Por ejemplo, la
función WriteLine() de la clase Console como debéis suponer "Escribe una Linea en la
consola". De forma similar se pueden declarar clases a las que se le añaden un número
ilimitado de métodos. En nuestro ejemplo 2.1 la única función que hemos definido lleva
el nombre de Main() la cual es una función especial que indica la entrada principal de
ejecución de un programa. Cada programa en C# debe tener una función Main().

Crear nuevos métodos o funciones


Para declarar un método o función utilizamos el siguiente formato:
[entorno] tipo_a_retornar Nombre_de_la_Función ([tipo Argumento1, tipo
Argumento2,...])
{
//lineas de código
}
las palabras dentro de corchetes [] son partes opcionales
de acuerdo con nuestro ejemplo 2.1, la función Main() cumple con el formato
establecido:
static void Main()
{
//lineas de código
}
• entorno: static
• tipo_a_retornar: void
• Nombre_de_la_Función: Main
• Argumentos: ninguno

En nuestro ejemplo la función Main() tiene como entorno la palabra clave static y como
tipo_a_retornar la palabra clave void. Cuando la función no retorna ningún tipo,
utilizamos la palabra void. Más adelante veremos más tipos además de void. La palabra
static también la veremos más adelante en nuestro estudio, por el momento debéis
confiar en el ejemplo y utilizarla aún sin saber lo que hace.
Podemos añadir a nuestro componente o clase un ilimitado número de funciones. Por
ejemplo, para añadir más funciones a nuestro primer ejemplo procederemos a crear una
función que suma dos valores. Como ya habéis aprendido en esta sección, crearemos
nuestra función de la forma establecida. Después del final de la función Main
crearemos una función con entorno static que retorne el tipo int, que se llame Suma y
que acepte dos argumentos de tipo int. Para hacerla funcionar vamos a llamarla desde
nuestra función principal:

Ejemplo 2.2 - Añadiendo funciones a nuestro programa


//Ejemplo 2.2 - Programa Hola Mundo con C# más adición

namespace Programa1
{
class HolaMundo
{
static void Main()
{
string var="Mundo";
System.Console.WriteLine ("Hola {0}!", var);
int num1 = 1;
int num2 = 3;
int resultado = Suma (num1, num2);
System.Console.WriteLine ("{0}+{1} = {2}", num1, num2, resultado);
}

static int Suma(int valor1, int valor2)


{
return (valor1+valor2);
}
}
}
probad vuestro programa, compiladlo y ejecutadlo. El resultado será el siguiente:
Hola Mundo!
1+3=4
Ésta es una pequeña introducción a funciones, más adelante estudiaremos más
detalladamente como crear y utilizar funciones.
Aplicaciones de consola
Las aplicaciones de consola no poseen una interfaz gráfica, no tienen botones o
ventanas, poseen una interfaz basada simplemente en texto. El ejemplo que hemos
realizado hasta ahora es una aplicación de consola que despliega texto en la pantalla,
para ello hemos utilizado la función WriteLine.
Como habíamos visto en la introducción, la plataforma .NET posee más de 4000
componentes cada cual con diferentes funciones internas. En el caso de nuestro
programa hemos usado la función WriteLine que se encuentra dentro del componente
Console. Para poder hacer uso de funciones estáticas que se encuentran en otros
componentes, en C# como en la mayoría de lenguajes de programación orientados a
objetos, debemos especificar el nombre del componente en primer lugar seguido por un
punto y a continuación en el nombre de la función. Es por esto que utilizamos la frase
Console.WriteLine. Dentro del componente Console la plataforma .NET tiene
disponibles muchísimas otras funciones que nos ayudan a diseñar programas de
consola. El lenguaje C# esta orientado con el paradigma de objetos y hereda muchos
elementos de C++.
[editar] Namespaces
Quizá algunos de vosotros os habréis preguntado ¿que significa la palabra System que
está al inicio de cada programa?. Púes bien, System en este caso representa un Espacio
de nombres (namespace en inglés).
Los espacios de nombres (namespaces) se crearon principalmente para dar más
organización a los componentes. La plataforma .NET tiene incorporados muchísimos
componentes y sería una tarea imposible tratar de memorizar todos los nombres de ellos
para no repetirlos. Tarde o temprano querréis crear un componente y no sabréis si el
nombre que queréis darle ya existe o no. Para evitarnos este problema y para poder
distinguir a dónde pertenecen ciertos componentes se han creado los espacios de
nombres.
Pongamos un ejemplo para comprender este concepto. Supongamos que deseamos crear
varios componentes para una institución educativa que se compone de educación
primaria y educación secundaria. ¿Cómo podríamos crear un componente que se llame
Presidente si existen 2 presidentes que tienen funciones distintas uno para la sección
primaria y otro para la sección secundaria? En este caso podríamos aplicar un espacio
de nombres para poder crear los componentes Presidente que cumplen distintas
funciones. Podríamos crear el espacio de nombres Primaria y dentro de éste el
componente Presidente. De igual forma el espacio de nombres Secundaria y dentro de
éste el componente Presidente cada uno componentes podrá tener definiciones distintas.
Para acceder a las funciones de los componentes Presidente podríamos usar:
Primaria.Presidente.nombre_de_la_función();
y
Secundaria.Presidente.otra_función();
Cada una de las dos definiciones de Presidente son independientes, no tienen relación
entre sí ya que pertenecen a dos espacios de nombre distintos.
De ésta forma podremos crear componentes y funciones con el nombre que deseemos
siempre y cuando especifiquemos a qué espacio de nombres pertenecen.
De acuerdo con la línea de código estudiada: System.Console.WriteLine ("Hola {0}!",
var); existe una función llamada WriteLine dentro del componente Console dentro del
nombre de espacio System. Cualquier otro componente llamado Console dentro de otro
espacio de nombres, es un componente diferente con funciones diferentes que quizás no
posea la función WriteLine.
Algo importante que debemos notar es que los espacios de nombres pueden tener sub-
espacios de nombres y estos a su vez sub-espacios de nombres. El objetivo de esto,
como lo hemos dicho, es mantener una organización de los componentes. Los espacios
de nombres, componentes y métodos se accederán de la misma forma como lo hemos
visto a través de un punto.
[editar] La palabra clave using
En ciertos proyectos tendremos que usar cierto espacio de nombres muchas veces.
Supongamos que estamos implementando un programa de consola y tenemos que usar
el componente Console repetidamente. Una forma de ahorrarnos escribir System varias
veces es especificar el espacio de nombres que vamos a usar al inicio de nuestro
programa con la palabra clave using. Por ejemplo en nuestro ejemplo 2.2 si añadimos la
línea de código using System; al inicio de nuestro programa, podemos llamar al
componente Console sin escribir System al inicio:
Ejemplo 2.2 Modificado - La palabra clave using
//Ejemplo 2.2 Modificado - La palabra clave ''using''

using System;

namespace Programa1
{
class HolaMundo
{
static void Main()
{
string var="Mundo";
Console.WriteLine ("Hola {0}!", var);
int num1 = 1;
int num2 = 3;
int resultado = Suma (num1, num2);
Console.WriteLine ("{0}+{1} = {2}", num1, num2, resultado);
}
static int Suma(int valor1, int valor2)
{
return valor1+valor2;
}
}
}
Comparando con nuestro ejemplo 2.2 original se ve en el ejemplo que ya no es
necesario poner la palabra system.Console.WriteLine, quedando Console.WriteLine
Algo importante para tener en cuenta es que la palabra clave using no puede ser
utilizada para ahorrarse el escribir el nombre de la clase. Por ejemplo la línea de código
using System.Console es inválida y producirá errores de compilación.
[editar] Caracteres sensibles
C# como todos los lenguajes de programación derivados de C hace diferencia entre
caracteres en mayúscula y caracteres en minúscula. Esto quiere decir que las palabras
Using y using son distintas y por lo tanto no cumplen la misma función. Debido a esto
debemos tener mucho cuidado cuando escribimos nuestros programas.
system.console.writeLine es diferente a system.Console.WriteLine y es diferente a
System.Console.WriteLine que es el nombre de espacio, componente y método que C#
incorpora para desplegar texto en la consola.
Finalizada esta pequeña introducción a nuestro primer programa en C#, pasamos al
capítulo 3 en donde examinaremos mas profundamente los fundamentos de C#.
• CAPÍTULO 3
o 1.1 Tipos
 1.1.1 Importancia de los tipos de datos
 1.1.2 Tipos en C#
 1.1.3 Tipos básicos o internos
 1.1.4 Escogiendo qué tipo usar
 1.1.5 Enteros
 1.1.6 Tipos de coma flotante
 1.1.7 El tipo decimal
 1.1.8 El tipo bool
 1.1.9 El tipo char
 1.1.10 Tipo Cadenas
 1.1.11 Convirtiendo tipos
o 1.2 Arreglos
o 1.3 Identificadores, Variables, Constantes y Enumeraciones
 1.3.1 Identificadores
 1.3.2 Variables
 1.3.3 Constantes
 1.3.4 Enumeraciones
o 1.4 Operadores
 1.4.1 Operadores matemáticos
 1.4.2 Operadores de asignación
 1.4.3 Operadores de comparación
 1.4.4 Operadores lógicos
 1.4.4.1 AND
 1.4.4.2 OR
 1.4.4.3 NOT
 1.4.4.4 XOR
 1.4.4.5 <<
 1.4.4.6 >>
 1.4.5 Operadores lógicos de unión
[editar] CAPÍTULO 3
En el capítulo 2 hemos introducido nuestro primer programa en C#, un programa
sencillo el cual incorpora muchos temas que hemos cubierto básicamente y solamente
en parte. En esta sección del libro procuraremos ver más a fondo las partes básicas de
C#. Nos internaremos más a fondo en la sintaxis y la estructura de C#.
En este capítulo cubriremos lo que son los tipos. Hablaremos de tipos básicos o internos
y de cómo crear nuevos tipos. También hablaremos en general de la manipulación de
datos. Hablaremos sobre condicionales, operadores matemáticos y varios otros temas
relacionados. Empecemos entonces nuestro estudio con lo que son tipos.
[editar] Tipos
[editar] Importancia de los tipos de datos
Los tipos son la base de cualquier programa. Un tipo no es más que un espacio en el que
se almacena una información, ya sean números, palabras o tu fecha de nacimiento.
[editar] Tipos en C#
C# es un lenguaje de tipeado seguro (o fuertemente tipado) lo cual quiere decir que el
programador debe definir a que tipo pertenece cada pedazo de información o cada
objeto que se crea. De esta forma podemos crear objetos de tipo número entero, de tipo
cadenas de texto, de tipo ventana, de tipo botones, entre otros. Haciendo esto, C# nos
ayudará a mantener nuestro código seguro en donde cada tipo cumple con su función.
En todas las operaciones el compilador comprueba los tipos para ver su compatibilidad.
Las operaciones no válidas no se compilan. De esta forma se evitan muchos errores y se
consigue una mayor fiabilidad. Esto también permite a C# anticipar de antemano la
cantidad de recursos del sistema que nuestro programa utilizará haciendo nuestro código
seguro y eficiente.
'Los tipos en C# al igual que C++ y Java se clasifican en dos secciones: Tipos básicos
o internos y tipos creados por el usuario. Los tipos básicos no son más que alias para
tipos predefinidos en la librería base de la plataforma .NET. Así, el tipo número entero
(que se representa con la palabra clave int), no es más que una forma rápida de escribir
System.Int32.
Dentro de estas dos secciones los tipos del lenguaje C# también son divididos en dos
grandes categorías: tipos por valor y tipos por referencia. Existe una tercera categoría de
tipos, disponible solo cuando se usa código no seguro: los punteros, que se discutirán
más adelante cuando hablemos de los objetos COM.
Los tipos por valor difieren de los tipos por referencia en que las variables de los tipos
por valor contienen directamente su valor, mientras que las variables de los tipos por
referencia almacenan la dirección donde se encuentran los objetos, es por eso que se las
llaman referencias. Más adelante describiremos como funcionan cada una de estas
categorías.
[editar] Tipos básicos o internos
Los tipos básicos como hemos dicho son espacios predefinidos y categorizados donde
se almacena información. En C# tenemos los siguientes tipos internos:
Tabla 3.1 - Tipos básicos
No
m
br
Va
e
By lor
pa Co
tes es
Ti ra n
uti qu
po la sig
liz e
C# pla no
ad so
taf ?
os po
or
rta
ma
.N
ET
true o false (verdadero o falso en
bool System.Boolean No 1
inglés)
byte System.Byte No 1 0 hasta 255
sbyte System.SByte Si 1 -128 hasta 127
short System.Int16 Si 2 -32.768 hasta 32.767
ushort System.Uint16 No 2 0 hasta 65535
-2.147.483.648 hasta
int System.Int32 Si 4
2.147.483.647
uint System.Uint32 No 4 0 hasta 4.394.967.395
-9.223.372.036.854.775.808
long System.Int64 Si 8 hasta
9.223.372.036.854.775.807
0 hasta
ulong System.Uint64 No 8
18446744073709551615
Ap
pr
ox
im
ad
a
m
en
te
±
1.
5E
-
Sy 45
st ha
e st
flo
m. Si 4 a
at
Si ±
ng 3.
le 4E
38
co
n
7
cif
ra
s
si
gn
ifi
ca
tiv
as
Approximadamente ±5.0E-
double System.Double Si 8 324 hasta ±1.7E308 con 7
cifras significativas
Approximadamente ±1.0E-28
decim
System.Decimal Si 12 hasta ±7.9E28 con 28 ó 29
al
cifras significativas
Cualquier carácter Unicode
char System.Char 2
(16 bits)
C# tiene una ventaja y característica especial sobre los demás lenguajes de
programación modernos y es que cada vez que se crea un objeto de un tipo básico, éstos
son mapeados internamente a un tipo primitivo de la plataforma .NET el cual es parte
del CLS (Especificación común del lenguaje) lo cual nos permite acceder y hacer uso de
estos desde cualquier lenguaje de la plataforma .NET. Es decir si es que creamos un
objeto de tipo int (entero) en C#, ese objeto podrá ser usado como tal dentro de J#,
JScript, Visual Basic .NET y cualquier otro lenguaje que conforme los requisitos de
.NET.
[editar] Escogiendo qué tipo usar
A la hora de programar deberéis decidir qué tipo de variables querréis usar.
Generalmente esta decisión se basa en el tipo de información que vayáis a usar y en el
tamaño de la información. Por ejemplo en nuestro ejemplo 2.2 del capítulo anterior
necesitábamos hacer la suma de dos valores numéricos por lo que usamos dos tipos
básicos de número entero (usando la palabra clave int) los cuales de acuerdo con
nuestra tabla 3.1 son números enteros (no pueden llevar valores decimales) y podrán
aceptar valores entre -2,147,483,648 y 2,147,483,647 lo cual es más que suficiente para
nuestro ejemplo de añadir dos números. En el caso de que necesitáramos hacer uso de
números reales (los cuales poseen una parte entera y una parte decimal como el número
10.22) podremos hacer uso del tipo float, double y decimal de acuerdo con el tamaño
del número que necesitemos y así cada uno de los tipos tiene su uso y capacidad de
acuerdo con la tabla 3.1.
A continuación explicaremos brevemente los tipos más usados en C#:
[editar] Enteros
Los tipos que sirven para almacenar números enteros son: byte, sbyte. short, ushort, int,
uint, long y ulong. Como se aprecia en la tabla, C# define versiones con y sin signo para
tipos con los mismos bytes utilizados. Cada tipo se distingue por la capacidad de
almacenaje.
Probablemente el tipo más utilizado es el int, púes se utiliza para controlar matrices,
indexar arreglos (arrays) además de las operaciones normales con enteros. Además, se
trata de un entero de tamaño medio: más pequeño que long y ulong, pero más grande
que byte, sbyte, short y ushort.
El siguiente ejemplo muestra la declaración y uso de algunos tipos enteros calculando el
número de segundos en una hora, dia y en un año.
Ejemplo 3.1 - utilizando tipos enteros (int)
// Ejemplo 3.1 - utilizando tipos enteros (int)
using System;
class Enteros{
public static void Main()
{
int minuto = 60; //segundos por minuto
int hora = minuto*60;
int dia = hora*24;
long anio = dia*365;
Console.WriteLine("Segundos en un dia: {0}", dia);
Console.WriteLine("Segundos en un año: {0}", anio);
}
}

De nuevo hemos usado el método Console.WriteLine para imprimir los resultados por
la consola. El identificador {0} dentro de la cadena de texto indica que se sustituye {0}
por el primer argumento. si hubiera más de un argumento, se seguiría con {1}, y así
sucesivamente. Por ejemplo, las dos líneas que utilizan Console.WriteLine se pueden
simplificar así:
Console.WriteLine("En un dia: {0}; en un año: {1}", dia, anio );
[editar] Tipos de coma flotante
Los tipos de coma flotante sirven para representar a números con parte fraccionaria. La
representación por supuesto puede no ser exacta, bien por errores de la máquina, bien
porque el número de decimales que se puede alojar es finito.
Existen tres clases de tipos de punto flotante : float, double y decimal. De los dos, el
más usado es double, púes es el valor que devuelven la mayoría de las funciones
matemáticas de la librería base.
El siguiente ejemplo calcula la raíz cuadrada y el logaritmo de dos:
Ejemplo 3.2 - utilizando tipos flotantes
// Ejemplo 3.2 - utilizando tipos flotantes
using System;

class Flotante{
public static void Main()
{
int a = 2;
double log2 = Math.Log(a);
double raiz2 = Math.Sqrt(a);
Console.WriteLine("El logaritmo de dos es {0}", log2 );
Console.WriteLine("La raiz de dos es {0}", raiz2 );
}
}
y la salida será la siguiente:
El logaritmo de dos es 0.693147180559945
La raiz de dos es 1.4142135623731

si intentamos cambiar el tipo de log2 a otro de menos precisión, como float o int, el
compilador protestará. Esto se debe, como hemos dicho a que el valor devuelto por
Math.Log() es de tipo double y si se quiere convertir a float, pués se perderán datos. Lo
mismo ocurre con la mayoría de los miembros de la clase Math, como Math.Sin(),
Math.Tan(), etc.
[editar] El tipo decimal
El tipo decimal es un tipo "nuevo" en el sentido de que no tiene equivalente en C/C++.
Es muy parecido a los tipo de coma flotante float y double.
En la aritmética de los tipos de coma flotante ordinarios, se pueden producir leves
errores de redondeo. El tipo decimal elimina estos errores y puede representar
correctamente hasta 28 lugares decimales. Esta capacidad para representar valores
decimales sin errores de redondeo lo hace especialmente eficaz para cálculos
monetarios.
[editar] El tipo bool
El tipo bool sirve para expresar los valores verdadero/falso, que en C# se muestran con
las palabras reservadas true y false.
En C#, por ejemplo, una instrucción de condición solo puede estar gobernada por un
valor bool, no como en C/C++, que lo puede estar también por un entero. De esta forma
se ayuda a eliminar el error tan frecuente en programadores de C/C++ cuando usa "="
en lugar de "==". En definitiva, la inclusión del tipo bool en el lenguaje ayuda a la
claridad del código y evita algunos errores muy comunes.
El siguiente ejemplo, muestra algunos usos del tipo bool:
Ejemplo 3.3 - utilizando tipos de decisión bool
// Ejemplo 3.1 - utilizando tipos de decisión bool
using System;
class Booleano{
public static void Main()
{
bool b;
b = true;
Console.WriteLine("b es {0}", b);
if(b)
{
Console.WriteLine("esto saldrá");
}
b = false;
if(b)
{
Console.WriteLine("esto no saldrá");
}
Console.WriteLine("2==2 es {0}", 2==2);
}
}

En la última línea se muestra que el operador "==" también devuelve un valor booleano.
El resultado debería ser el siguiente:
b es True
esto saldrá
2==2 es True
[editar] El tipo char
El tipo char permite almacenar un carácter en formato simple, unicode de 16 bits o
caracteres de escape. Usando el formato unicode nos garantiza que los acentos se ven de
forma adecuada y además permite la representación de otros alfabetos, como el japonés,
griego, cirílico, etc. Para introducir un carácter se utilizan comillas simples, de forma
que declarar un carácter sigue la estructura
char letra1 = 'a'; //formato simple
char letra2 = '\u0041'; //formato Unicode que representa la letra A
char letra3 = '\n'; formato carácter de escape
Para una lista completa de caracteres unicode podréis visitar la siguiente página:
http://unicode.coeurlumiere.com/
La siguiente lista contiene los caracteres de escape comunes y su significado:
\' apostrofe
\" Comillas
\\ Backslash
\0 Null (nulo)
\a Alerta
\b Retroceso
\f Form feed
\n Línea nueva
\r Retorno del carro
\t Tabulación Horizontal
\v Tabulación Vertical
[editar] Tipo Cadenas
Los tipos cadena (palabra clave string) son tipos que almacenan un grupo de caracteres.
En C# los tipos cadena se crean con la palabra clave string seguido por el nombre de la
variable que deseamos instanciar. Para asignar un valor a este tipo debemos hacerlo
entre comillas de la siguiente forma:
string miCadena = "Esta es una cadena de caracteres";
Debido a que el tipo cadena (string) es uno de los tipos más usados en C#, lo
estudiaremos detalladamente más adelante.
[editar] Convirtiendo tipos
En nuestros programas muchas veces necesitaremos cambiar de tipo a los objetos que
hayamos creado. Esto lo podremos hacer implícitamente o explícitamente. Una
conversión de tipos implícita sucede automáticamente, es decir el compilador se hará
cargo de esto. Una conversión explicita en cambio se llevará a cabo únicamente cuando
nosotros lo especifiquemos. Hay que tomar en cuenta que no siempre podremos hacer
una conversión de un tipo hacia otro.
Como regla general las conversiones implícitas se llevan a cabo cuando se desea
cambiar un tipo de menor capacidad hacia un tipo de mayor capacidad de la misma
especie. Por ejemplo si deseamos crear 2 tipos enteros (misma clase) el uno que lleve el
tipo short (menor capacidad) y el otro que lleve el tipo int (mayor capacidad) una
conversión implicita de short a int se lleva a cabo en el siguiente ejemplo:
short corto = 3;
int entero = corto; //compilará sin ningún problema
aquí sucede una conversión implícita, el valor de la variable corto (en este caso 3) que
es de tipo short es asignado a la variable de tipo int sin que el compilador nos de
ningún problema ya que hará una conversión de short a int implícitamente por nosotros
debido a la regla anteriormente citada.
En el caso que queramos hacer de forma inversa, es decir asignar un valor int a una
variable short, estaríamos violando la regla de asignar un tipo de menor capacidad a una
variable de tipo de mayor capacidad aunque sean de la misma clase (enteros). Asi el
siguiente ejemplo no compilará dándonos un error:
int entero = 300;
short corto = entero; //nos dará un error de compilación
En estos casos es cuando podremos hacer una conversión explícita. Debido a que la
información almacenada en la variable entero de tipo int está también en el rango de
capacidad del tipo short y los dos tipos son de la misma clase (enteros) podremos hacer
una conversión explicita designando entre paréntesis a que tipo queremos convertir de
la siguiente manera:
int entero = 300;
short corto = (short) entero; //convertirá la variable entero para que sea del tipo short
En el ejemplo anterior, el compilador no nos dará ningún problema. Cada uno de los
tipos básicos citados a continuación soportará una conversión implícita o explícita como
se lo expresa en la siguiente tabla:
Conversión Implicita (I) Conversión Explícita (E)
De
sd us
by sb sh
e/h ho int ui
te yte ort
aci rt
a
byt
I E I I I I
e
sb
E I I E I E
yte
sh
E E I E I E
ort
us
hor E E E I I
t

int E

uint E

long E

ulong E

float E

double E

de
ci
E E E E E E
ma
l
[editar] Arreglos
En C# se pueden construir arreglos de prácticamente cualquier tipo de dato. Los
arreglos, también llamados vectores o arrays, no son más que una sucesión de datos del
mismo tipo. Por ejemplo, el concepto matemático de vector es una sucesión de números
y por lo tanto es un arreglo unidimensional. Así, podemos construir arreglos de objetos,
de cadenas de texto, y, por supuesto, arreglos de enteros:
using System;

class Arreglo{
public static void Main()
{
int[] arr = new int[3];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
Console.WriteLine( arr[1] );
}
}
Para crear un arreglo debemos especificar de qué tipo deseamos crear el arreglo seguido
por corchetes [ ] que es el distintivo del arreglo (en nuestro ejemplo usamos int[]),
seguido por la palabra clave new y el tipo y la cantidad de parámetros que tendrá
nuestro arreglo. En el ejemplo anterior, por ejemplo, se creó un arreglo arr
unidimensional con capacidad para 3 enteros (especificado por new int[3]), y luego se le
asignó a cada parámetro un entero distinto (nótese que se comienza a contar a partir de 0
y el número del parametro se encuentran entre corchetes).
Existe una forma más corta para declarar el arreglo y asignarle los valores:
int[] arr = {1,2,3}; //es exactamente lo mismo que el ejemplo anterior

También se pueden crear arreglos bidimensionales (y de la misma forma para más


dimensiones). En ese caso la sintaxis para declarar un arreglo bidimensional de enteros
será
int[,] arr; //declaración de arreglos bidimensionales en C#
en contraposición a C/C++, en el que se declararía como
int[][] arr; //declaración de arreglos bidimensionales en C/C++
De esta forma, un arreglo bidimensional se declararía y utilizaría de la siguiente forma:
using System;
class Arreglo2{
public static void Main()
{
int[,] arr = new int[2,2];
arr[0,0] = 1;
arr[1,0] = 2;
arr[0,1] = 3;
arr[1,1] = 4;
Console.WriteLine("El valor que posee la variable arr[1,1] es {0}", arr[1,1] );
}
}
el resultado será:
El valor que posee la variable arr[1,1] es 4

igual que el ejemplo anterior, podemos declarar todo el arreglo de la siguiente forma:
int[,] arr = {{1,2},{3,4}};
Hablaremos más sobre arreglos más adelante
[editar] Identificadores, Variables, Constantes y Enumeraciones
[editar] Identificadores
En una aplicación siempre se deben crear variables, constantes, métodos, objetos, etc.
Para poder crearlos debemos asignar nombres o identificadores. Estos identificadores
deben seguir ciertas reglas:
1. Un identificador DEBE empezar con una letra o un signo _
2. Un identificador NO puede tener espacios en blanco
3. Un identificador NO puede llevar el mismo nombre que una palabra clave
Después de que el identificador empiece con una letra o un simbolo _, el identificador
puede tener cualquier cantidad de letras, líneas o números.
Ejemplo:
EsteIdentificadorEsValido
_este_también
esteEsOtro1
esteEsOtro2
como ejemplos inválidos tenemos:
Esto es invalido
123_Otro_inválido
int
en los ejemplos que no son válidos, el primero contiene espacios, el segundo empieza
con números y el tercero es una palabra clave lo cual no es permitido.

Nota: Es recomendado que las variables siempre empiecen con minusculas y que sigan
el patrón llamado Camello es decir que el nombre de la variable que tenga varias
palabras debe ser formado de la siguiente manera: la primera palabra empezará con
minúsculas pero la segunda, tercera, etc palabras estarán unidas a la primera y tendrán
su primera letra en mayúsculas ejemplo: miVariable. También se ha recomendado que
el nombre de Métodos, Clases y demás nombres que necesitamos especificar deberán
llevar el mismo formato anterior con la excepción de que la Primera letra deberá ser
mayúscula.

[editar] Variables
Una variable es el nombre que se le da al espacio donde se almacena la información de
los tipos. Las variables pueden llevar cualquier nombre que deseemos, eso sí no se
podrá hacer uso de palabras claves de C#. En los ejemplos anteriores hemos usado
varias variables con diferentes tipos y diferentes valores.
Para crear una variable debemos especificar a qué tipo pertenece antes del nombre que
le vamos a dar. Por ejemplo si deseamos crear una variable que se llame var y que sea
del tipo entero (int) procederemos de la siguiente manera:
int var;
Una vez creada la variable podemos almacenar la información que deseamos. Para
hacerlo utilizamos el símbolo = después del nombre de la variable.
var = 10;
Hay que tener presente que la variable como su nombre lo indica podrá tomar otros
valores. Por ejemplo si deseamos que nuestra variable cambie de valor a 5 hacemos lo
que habíamos hecho en el ejemplo anterior pero con el nuevo valor:
var = 5;
De ahora en adelante la variable var pasa a tener el valor de 5.
Para simplificar el proceso de creación y asignación de valor de una variable podemos
especificar estos dos procesos en una misma línea.
int var = 10;
La primera vez que una variable recibe un valor se llama inicialización de la variable.
En C# todas las variables que van a ser utilizadas deben ser inicializadas para que
vuestro programa pueda funcionar.
En caso anterior hemos creado la variable var e inicializado la variable con el valor 10.
Hay que tener cuidado que la creación de una variable se la hace 1 sola vez y la
asignación de diferentes valores se puede hacer cuantas veces queramos.
[editar] Constantes
Las constantes como su nombre lo indica son variables cuyo valor no puede ser
alterado. Éstas se utilizan para definir valores que no cambian con el tiempo. Por
ejemplo podemos definir una constante para especificar cuantos segundos hay en una
hora de la siguiente forma:
const int segPorHora = 3600;
Esta constante no podrá ser cambiada a lo largo de nuestro programa. En el caso de que
queramos asignarle otro valor, el compilador nos dará un error.
Las constantes deben ser inicializadas en el momento de su creación.
[editar] Enumeraciones
Supongamos que estamos diseñando un juego y necesitamos crear una variable para
saber cuál es el estado del tanque de combustible de nuestro automóvil. Suponiendo que
tenemos 4 niveles en el tanque: lleno, medio, bajo y crítico. ¿Qué tipo de variable
podríamos usar para especificar estos estados? Una forma de hacerlo podría ser si
especificamos una variable de tipo int (entero) que tome los valores de 1 para lleno, 2
para medio, 3 para bajo y 4 para crítico. Esta alternativa funcionaría pero a la larga nos
olvidaremos qué número representaba qué. Una forma muy elegante de solucionar este
problema es utilizando el tipo enum o enumeraciones. Veamos el siguiente ejemplo
para comprender este concepto:
Ejemplo 3.1 - Control del estado de combustible
using System;
namespace Autos
{
class Control
{
enum tanque
{
lleno,
medio,
bajo,
critico,
}

static void Main()


{
tanque auto1 = tanque.lleno;
RevisarEstadoTanque(auto1);

auto1 = tanque.critico;
RevisarEstadoTanque(auto1);
}

static void RevisarEstadoTanque(tanque auto)


{
if (auto==tanque.lleno)
Console.WriteLine ("¡El tanque está lleno!");
if (auto==tanque.medio)
Console.WriteLine ("El tanque está por la mitad");
if (auto==tanque.bajo)
Console.WriteLine ("¡El tanque está casi vacío!");
if (auto==tanque.critico)
Console.WriteLine ("¡Alerta! tu auto se quedó sin combustible");
}

}
Este programa sencillo crea una enumeración llamada tanque y dentro de ella crea 4
constantes: lleno, medio, bajo, y critico. Dentro de nuestro programa creamos la
variable de tipo tanque llamada auto1 la cual podrá tomar los valores especificados
dentro de la enumeración. Cuando asignamos a la variable auto1 el valor de
tanque.lleno y revisamos el estado del tanque llamando a la función
RevisarEstadoTanque, podremos comprobar cuál es el estado actual del tanque de
combustible. Ésta es una forma muy descriptiva de cómo crear variables que cambien
de estado, una solución elegante y sencilla a nuestro problema.
En realidad las constantes dentro de las enumeraciones tienen valores enteros asignados.
Estos valores pueden ser inicializados con distintos valores que nosotros deseemos e
incluso podemos especificar qué tipo de entero queremos usar. En nuestro ejemplo
anterior los valores de la enumeración son valores enteros del tipo int el primer
elemento dentro de la enumeración tiene asignado el valor de 0, el siguiente el valor 1 y
así sucesivamente. Esto sucede cuando no especificamos a qué tipo queremos inicializar
nuestra enumeración y tampoco asignamos valores a las constantes. En el siguiente
ejemplo podemos ver cómo especificar otro tipo de constantes y otros valores.
Supongamos que queremos especificar cuantos segundos hay en un minuto, cuantos
segundos hay en una hora y cuantos segundos hay en 24 horas. Con enumeraciones lo
podemos hacer de la siguiente manera:
Ejemplo 3.2 - Enumeraciones
using System;
namespace Ejemplos
{
class Enumeraciones
{
enum segundos :uint
{
minuto = 60,
hora = 3600,
dia = 86400,
}
static void Main()
{
Console.WriteLine("Existen {0} segundos en 1 minuto, {1} segundos en 1 hora y
{2} segundos en 24 horas",(uint)segundos.minuto, (uint)segundos.hora,
(uint)segundos.dia);
}
}
}
El resultado es el siguiente:
Existen 60 segundos en 1 minuto, 3600 segundos en 1 hora y 86400 segundos en 24
horas
El ejemplo anterior nos muestra otra forma de usar una enumeración. Hay que tener en
cuenta que el tipo que va a tener la enumeración se encuentra después del nombre de la
enumeración precedido por dos puntos. De esa forma podremos especificar de qué tipo
son. Como habíamos dicho anteriormente se podrá utilizar cualquier tipo de la clase
enteros como byte, sbyte, short, ushort, int, uint, long o ulong. En el caso de que no se
especifique a qué tipo pertenece el compilador le dará el tipo int. También se debe
tomar en cuenta que los valores de las constantes están asignados con el signo = y están
separadas por comas.
Como habéis visto la forma de acceder al valor numérico de las enumeraciones es
especificando entre parentesis a qué tipo pertenecen, en este caso (uint). Después de lo
cual especificamos el nombre de la enumeración seguido por un punto que separa al
nombre de la constante. En el caso de que deseemos desplegar sólo el nombre de la
constante y no su valor, se debe omitir el nombre del tipo como: segundos.hora sin
(uint) al inicio.
En el caso de que solamente especifiquemos algunos valores de las constantes, el
compilador asignará el siguiente valor a la siguiente constante. Así por ejemplo:
enum números
{
uno, //toma el valor de 0
dos, //toma el valor de 1
diez = 10, //toma el valor de 10
once, //toma el valor de 11
}
[editar] Operadores
Los operadores son símbolos con los cuales C# tomará una acción. Por ejemplo existen
operadores matemáticos para sumar, restar, multiplicar y dividir números. Existen
también operadores de comparación que analizará si un valor es igual, mayor o menor
que otro y operadores de asignación los cuales asignarán nuevos valores a los objetos o
variables. A continuación explicaremos un poco más detalladamente los operadores en
C#:
[editar] Operadores matemáticos
Casi todos los lenguajes de programación soportan operadores matemáticos. Estos
operadores se utilizan para realizar operaciones matemáticas sencillas entre números.
Entre estos operadores tenemos los de suma, resta, multiplicación, división y módulo (o
residuo): +,-,*,/,%, y se los usa de la siguiente manera:
using System;

class operadoresMatematicos
{
public static void Main()
{
int a = 7;
int b = 4;
int c = a + b;
int d = a - b;
int e = a * b;
int f = a / b;
int g = a % b;
Console.WriteLine ("De los números: {0} y {1} la suma es: {2}, la resta es:
{3}, la multiplicación es: {4}, la división es: {5} con un residuo de: {6}",a,b,c,d,e,f,g);
}
}
[editar] Operadores de asignación
Los operadores de asignación son aquellos que sirven para asignar el valor del objeto o
variable de la derecha al objeto o variable de la izquierda. Un ejemplo sencillo de este
tipo de operadores es la inicialización de variables. Como habíamos visto, para asignar
el valor a una variable simplemente utilizamos el símbolo (u operador) igual =
int a = 15; //la variable a tomará el valor de 15.
int b = a = 10; //la variable a y la variable b tomarán el valor de 10.
Además de estos operadores de asignación sencillos, existen otros operadores de
asignación que realizan operaciones matemáticas antes de asignar el valor a la variable
u objeto. Entre ellos tenemos: +=, -=, *=, /=, %=, ++, --. Ejemplos:
var += 10; // realiza la operación var = var+10;
var -= 10; // realiza la operación var = var-10;
var *= 10; // realiza la operación var = var*10;
var /= 10; // realiza la operación var = var/10;
var++; //realiza la operacion var = var+1; después de procesar esta línea
++var; //realiza la operación var = var+1; antes de procesar esta línea
var--; //realiza la operacion var = var-1; después de procesar esta línea
--var; //realiza la operación var = var-1; antes de procesar esta línea
[editar] Operadores de comparación
Estos operadores son múy utiles cuando tenemos que cambiar el flujo de nuestro
programa. Con ellos podemos comparar si un objeto o variable es igual (==), no es igual
(!=), es mayor o igual (>=), es menor o igual (<=), es mayor (>) o es menor (<) que otro
objeto. El resultado de esta comparación es de tipo bool es decir verdadero o falso (true
o false). Estos operadores se los usa de la siguiente forma:
int a = 10;
int b = 20;
bool resp;

resp = (a == b); // compara si a es igual a b y retorna el valor bool false (o falso), tómese
en cuenta que a==b es MUY diferente a a=b
resp = (a != b); // compara si a es diferente a b y retorna el valor bool true (o verdadero)
resp = (a <= b); // compara si a es menor o igual a b y retorna el valor bool true (o
verdadero)
resp = (a >= b); // compara si a es mayor o igual a b y retorna el valor bool false (o
falso)
resp = (a < b); // compara si a es menor a b y retorna el valor bool true (o verdadero)
resp = (a > b); // compara si a es mayor a b y retorna el valor bool false (o falso)
[editar] Operadores lógicos
Para entender como funcionan los operadores lógicos tenemos que aprender un poco lo
que son los números binarios. En esta parte del libro no cubriremos en detalle este
extenso tema de los números binarios ni del Algebra que gobierna estos números ni
mucho menos de como se comportan las puertas lógicas dentro de un ordenador porque
nos tomaría uno o dos libros completos, pero nos gustaría dar un poco de bases de como
es que los números binarios forman parte de los operadores lógicos. Toda información
que el ordenador opera internamente es representada por números binarios (por unos y
ceros que son conocidos también por verdadero y falso), así la letra A el ordenador
internamente lo representa en código binaro ASCII como 01000001 que en números
"normales" o decimales es 65. Para manipular esta información en unos y ceros, el
ordernador tiene operadores lógicos los cuales permiten cambiar la información de una
manera que nos convenga. Por medio de estos operadores lógicos el ordenador es capáz
de tomar decisiones, procesar cualquier información, hacer complicadas operaciones
matemáticas, o en otras palabras, por medio de estos operadores lógicos, el ordenador
hace todo lo que vosotros le habéis visto hacer.
Los operadores lógicos más importantes para nuestro estudio en C# son:
[editar] AND
Representado por el simbolo &. Comprueba si todos los números binarios son 1 (o
verdadero) entonces la respuesta es 1 (o verdadero)
[editar] OR
Representado por el simbolo | (barra vertical de la tecla del 1). Comprueba si cualquiera
de los números binarios es 1 (o verdadero) entonces la respuesta es 1 (o verdadero)
[editar] NOT
Representado por el simbolo ~ y !. Invierte la respuesta. En operaciones con tipos bool,
el operador ! cambia la variable de verdadero a falso o viceversa, pero en números
binarios, el operador ~ cambia cada uno de los unos y ceros por su opuesto, cuando
encuentra un uno lo cambia por un cero y viceversa, así por ejemplo si tenemos el
número binario 01000001 y aplicamos el operador NOT ~ obtendremos 10111110, pero
si tenemos una expresion que se evalua como true (o verdadera) y si se aplica el
operador !, se obtiene una respuesta false (o falsa). Por ejemplo (!(10==10)) esta
expresión tiene como resultado false
[editar] XOR
Representado por el simbolo ^. En dos números, comprueba si los dos números binarios
son iguales, entonces la respuesta es 0 (o falso).
[editar] <<
Desplazar a la izquierda desplaza todos los bits hacia la izquierda introduciendo ceros al
final de la derecha y descartando los últimos números. Asi el número 01000001 si se lo
desplaza a la izquierda una vez 01000001 << 1, se convierte en 10000010
[editar] >>
Al igual que el operador anterior, desplazar a la derecha desplaza todos los bits hacia la
derecha introduciendo ceros al final de la izquierda y descartando los últimos números.
Asi el número 01000001 si se lo desplaza a la derecha una vez 01000001 >> 1, se
convierte en 00100000
[editar] Operadores lógicos de unión
En el caso de que deseemos comparar varios valores para saber si todos son verdaderos
o si alguno es verdadero podemos usar los operadores lógicos de unión && y ||
a && b // esta línea compara si a y b son verdaderos retorna el valor true (o verdadero)
si los dos lo son
a || b // esta línea compara si a o b son verdaderos retorna el valor true (o verdadero) si
alguno de los dos es
!a // esta línea compara si a es verdadero retorna falso si lo es y viceversa.
a y b pueden representar variables, constantes, números, funciones, expresiones, etc.
que den como resultado un valor de decisión (true o false). Asi por ejemplo, el siguiente
ejemplo es válido:
int a = 0;
int b = 10;
int c = 20;

if ((a <= b) && (c >= b))


System.Console.WriteLine ("a es menor o igual a b y c es mayor o igual a b");

• 1 Estructuras de control
o 1.1 Saltos incondicionales
 1.1.1 La sentencia goto
o 1.2 Saltos condicionales
 1.2.1 Instrucción if
 1.2.2 Instrucción switch
 1.2.3 Bucle for
 1.2.4 Bucle while
 1.2.5 Bucle do-while
 1.2.6 Bucle foreach
 1.2.7 Usando continue y break
[editar] 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.
• 1 Introducción a las clases en C#
o 1.1 Métodos
o 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
o 1.3 Modificadores public y static
o 1.4 Constructores e instancias de una clase
o 1.5 Sobrecarga de métodos
o 1.6 La palabra reservada this
• 2 Propiedades e indizadores
o 2.1 Propiedades
o 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.

• 1 Herencia
o 1.1 La palabra reservada base
o 1.2 Clases Abstractas
o 1.3 Miembros virtual
o 1.4 Problemas propuestos
[editar] 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#.

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;
Console.WriteLine("H1T 7NY K3Y T0 C0NT1NU3 =/");
Console.ReadKey();
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;
}
}
}
•1 Sobrecarga de operadores
•2 ¿Qué es la sobrecarga de operadores?
•3 Sobrecargando operadores en la práctica
•4 Operadores binarios
•5 Operadores Unarios
[editar] 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.
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
• 1 Definición
• 2 Obteniendo Referencias a la Interfaz
• 3 Pasar interfaces como parámetros
• 4 Implementación Explícita de una Interfaz
• 5 Jerarquías de interfaces
[editar] 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);
Hay varias formas de agrupar conjuntos de datos en C#.
[editar] Enumeraciones
La palabra clave enum se utiliza para declarar una enumeración, un tipo que consiste en
un conjunto de constantes con el nombre de lista de enumeradores. Cada tipo de
enumeración tiene un tipo subyacente, pudiendo estos ser cualquier tipo integral menos
char. El tipo predeterminado de los elementos de la enumeración es int. Por defecto el
primer enumerador tiene el valor 0 y el valor sucesivo se incrementa en 1. Ejemplo:
public enum Dias
{
Domingo,
Lunes,
Martes,
Miercoles,
Jueves,
Viernes,
Sabado
}
En esta enumeración Domingo es 0, Lunes es 1 y así sucesivamente. Los enumeradores
pueden tener inicializadores reemplazando estos a los valores predeterminados, por
ejemplo:
public enum Dias
{
Domingo = 1,
Lunes,
Martes,
Miercoles,
Jueves,
Viernes,
Sabado
}
En este caso obligamos a la enumeración a comenzar desde 1 en lugar de 0.
Dijimos que el tipo subyacente de las enumeraciones es int, sin embargo se necesita una
conversión explícita para pasar un valor enum a un valor int, así:
int x = (int)Dias.Lunes;
En este caso el resultado sería que x valdría 2.

You might also like