Curso C#

Descargar como docx, pdf o txt
Descargar como docx, pdf o txt
Está en la página 1de 87

TUTORIAL DE C#

Conceptos Básicos
 ¿Qué es C#?

C# es un elegante lenguaje orientado a objetos que permite a los desarrolladores


construir una variedad de aplicaciones seguras y robustas que son ejecutadas en el
Framework .NET.

Puedes utilizar C# para crear aplicaciones para Windows, servicios Web, aplicaciones
móviles, aplicaciones cliente-servidor, aplicaciones de base de datos, y mucho, mucho
más.
- El Framework .NET

¡Aprenderán más acerca de estos conceptos en las próximas lecciones!

El Framework .NET está compuesto por el Entorno en Tiempo de Ejecución de


Lenguaje común (o CLR por sus siglas en inglés) y la Librería de Clase del
Framework .NET.

El CLR es el fundamento del Framework .NET. Se encarga de administrar el código en


tiempo de ejecución, proveyendo servicios medulares como administración de
memoria, exactitud de código y muchos otros aspectos para tu código.

La librería de clases es una colección de clases, interfaces y tipos de valor que te


permiten lograr un amplio rango de tareas de programación comunes, como
recolección de datos, acceso a archivos y trabajar con texto.

Los programas en C# utilizan intensamente la librería de clases del Framework .NET


para realizar tareas comunes y proveer múltiples funcionalidades.

Estos conceptos pueden parecer complejos, pero por ahora, solo recuerda que las
aplicaciones escritas en C# utilizan el Framework .NET y sus componentes
 Variables

Los programas usualmente utilizan datos para realizar actividades.

Al crear una variable se reserva una ubicación en memoria, o un espacio en memoria,


para almacenar valores. Se le llama variable porque la información almacenada en esa

Por ejemplo, firstName y lastName son buenos nombres descriptivos de variables,


mientras que abc y xyz no lo son

ubicación puede ser modificada cuando el programa es ejecutado.

Para utilizar una variable, primero debe ser declarada especificando el nombre y el tipo de
dato.

Un nombre de variable, también llamado un identificador, puede contener letras,


números y el carácter de guion bajo (_) y debe comenzar con una letra o un guion.

A pesar que el nombre de la variable puede ser cualquier conjunto de letras y números, el
mejor identificador es descriptivo del dato que contiene. ¡Esto es muy importante para
poder crear código limpio, comprensible y legible!
- Tipos de Variables

Un tipo de dato define la información que puede ser almacenada en una variable, el
tamaño que necesitas en memoria y las operaciones que pueden ser realizadas con la
variable.

Por ejemplo, para almacenar un valor entero (un número entero) en una variable,
utiliza la palabra int:

El código anterior declara una variable llamada myAge del tipo integer.

Puedes asignar el valor de una variable cuando la declaras:

O posteriormente en tu código:

Una línea de código que completa una acción es llamada una declaración. Cada
declaración en C# debe terminar con un punto y coma.
int myAge = 18;
int myAge;

myAge = 18;

- Tipos de datoRecuerda
int myAge; incorporados
que necesitas declarar la variable antes de utilizarla.

int x = 42;
Hay varios tipos de datos incorporados en C# por defecto. Los más comunes son:
double pi = 3.14;
int - entero.
char-ynúmero
float = ‘Z’; de punto flotante.

bool isOnline = true;

string firstName = “David”;


double - versión de doble precisión de float.
char - un sólo carácter.
bool - Booleano que sólo puede tener dos valores: True (Verdadero) o False (Falso).
string - una secuencia de caracteres.

Las siguientes declaraciones utilizan los tipos de datos de C#:


 Tu primer programa de C #

Para crear un programa C #, debe instalar un entorno de desarrollo integrado (IDE) con
herramientas de codificación y depuración.
Utilizaremos Visual Studio Community Edition, que está disponible para descargar de
forma gratuita.
Después de instalarlo, elija la configuración predeterminada.
Luego, haga clic en Archivo-> Nuevo-> Proyecto y luego elija Aplicación de consola como
se muestra a continuación:

Ingrese un nombre para su Proyecto y haga clic en Aceptar.

La Aplicación using
de consola usa una interfaz de solo texto. Elegimos este tipo de
System;
aplicación para centrarnos en aprender
using System. Collections. los fundamentos de C #.
Generic;
using System. Linq;
Visual Studio generará automáticamente
using System. Text; algo de código para su proyecto:
using System. Threading. Tasks;
Aprenderá lo que hace cada una de las declaraciones en las próximas elecciones.
Por ahora, recuerde namespace
que cada aplicación
SoloLearnde consola de C # debe contener un método (una
función) llamado Main. Main
{ es el punto de partida de cada aplicación, es decir, el punto
desde el cual nuestro programa
classcomienza
Program la ejecución.
{
static void Main (string [] args)
{
}
}
}
Para ejecutar su programa, presione Ctrl + F5. Verá la siguiente pantalla: 

Esta es una ventana de consola. Como no teníamos ninguna declaración en


nuestro método Main, el programa solo produce un mensaje general. Al presionar
cualquier tecla se cerrará la consola.

 Imprimir Texto
- Mostrar salida

La mayoría de las aplicaciones requieren alguna entrada del usuario y, como


resultado, dan salida.
Para mostrar texto en la ventana de la consola, utilice los métodos Console.Write o
Console.WriteLine. La diferencia entre estos dos es que Console.WriteLine es seguido
por un terminador de línea, que mueve el cursor a la siguiente línea después de la
salida de texto.
El siguiente programa mostrará ¡Hola Mundo! a la ventana de la consola:

static void Main (string [] args)


{

Console. WriteLine (“¡Hola Mundo!”);


}

Tenga en cuenta los paréntesis después del método WriteLine. Esta es la forma de


pasar datos o argumentos a los métodos. En nuestro caso, WriteLine es el método y
pasamos "¡Hola Mundo!" a eso como argumento. Los argumentos de cadena deben
estar entre comillas.

static void Main (string [] args)


{

int x = 89;

Console. WriteLine (x);


}

// Outputs 89
Podemos mostrar valores variables en la ventana de la consola:
Para mostrar una cadena de texto formateada, use la siguiente sintaxis:

static void Main (string [] args)


{

int x = 10;

double y = 20;

Console. WriteLine (“x = {0}; y = {1}”, x, y);


}

// Output: x = 10; y = 20

Como puede ver, el valor de x reemplazó a {0} y el valor de y reemplazó a {1}.

Puede tener tantos marcadores de posición de variables como necesite. (es decir: {3},
{4}, etc.).
 Obteniendo la entrada del usuario
- Entrada del usuario

También puede solicitar al usuario que ingrese datos y luego use


el método Console.ReadLine para asignar la entrada a una variable tipo string. 
El siguiente ejemplo le pide al usuario un nombre y luego muestra un mensaje que
incluye la entrada:
El método Console.ReadLine espera la entrada del usuario y luego lo asigna a la

static void Main (string [] args)


{

string yourName ;

Console. WriteLine (“¿Cuál es tu nombre?”);

yourName = Console. ReadLine();

Console. WriteLine (“Hola {0}”, yourName);


}

variable. La siguiente declaración muestra una cadena con formato que contiene


“Hola” con la entrada del usuario. Por ejemplo, si ingresas David, la salida será Hola
David.

Tenga en cuenta los paréntesis vacíos en el método ReadLine. Esto significa que no
toma ningún argumento.

El método Console.ReadLine() devuelve un valor string. 


Si espera otro tipo de valor (como int o double), los datos ingresados deben
convertirse a ese tipo. 
Esto se puede hacer utilizando los métodos Convert.ToXXX, donde XXX es el
nombre .NET del tipo al que queremos convertir. Por ejemplo, los métodos
incluyen Convert.ToDouble y Convert.ToBoolean. 
Para la conversión de enteros, hay tres alternativas disponibles en función del tamaño
de bits del entero: Convert.ToInt16, Convert.ToInt32 y
Convert.ToInt64. El tipo int predeterminado en C # es de 32 bits.
Creemos un programa que tome un número entero como entrada y lo muestre en un
mensaje:

Los comentarios son declaraciones explicativas que puede incluir en un programa para


beneficiar al lector de su código.
El compilador ignora todo lo que aparece en el comentario, por lo que ninguna de esa
información afecta el resultado.
Un comentario que comienza con dos barras diagonales (//) se denomina comentario de
una sola línea. Las barras indican al compilador que ignore todo lo que sigue, hasta el final
de la línea.
- Comentarios multilínea
Los comentarios que requieren varias líneas comienzan con / * y terminan con * / al
final del bloque de comentarios.
Puede colocarlos en la misma línea o insertar una o más líneas entre ellos.
 La palabra clave var

/ * Algún
texto de comentario largo
*/
int x = 42;
Console.WriteLine (x);

var num = 15; comentarios a su código es una buena práctica de programación. Facilita una
Agregar
comprensión clara del código para usted y para otros que lo lean.

El código anterior hace que el compilador determine el tipo de la variable. Como el


valor asignado a la variable es un entero, la variable se declarará como
un entero automáticamente.

Una variable puede declararse explícitamente con su tipo antes de usarse.


Alternativamente, C # proporciona una función práctica para permitir que el compilador
determine el tipo de variable automáticamente en función de la expresión a la que está
asignado.
La palabra clave var se usa para esos escenarios:
Las variables declaradas usando la palabra clave var se denominan variables de tipo
implícito.
Las variables de tipo implícito deben inicializarse con un valor.
Por ejemplo, el siguiente programa causará un error:

var num;
num = 42;

Aunque es fácil y conveniente declarar variables usando la palabra clave var, el uso


excesivo puede dañar la legibilidad de su código. La mejor práctica es declarar
explícitamente las variables.

 Constantes
Las constantes almacenan un valor que no se puede cambiar desde su asignación inicial.
Para declarar una constante, use el modificador const.
Por ejemplo:

const double PI = 3.14;

El valor de la constante PI no se puede cambiar durante la ejecución del programa.


Por ejemplo, una declaración de asignación posterior en el programa causará un error:
 Operadores
const double PI = 3.14;
PI = 8; // error

Las constantes deben inicializarse con un valor cuando se declaran.

Un operador es un símbolo que realiza manipulaciones matemáticas o lógicas.


- Operadores aritméticos

C # admite los siguientes operadores aritméticos: por ejemplo:

- División

int x = 10 / 4;
Console.WriteLine (x);

// Outputs 2

La división por 0 no está definida y detendrá la ejecución de tu programa.

int x = 10;
int y = 4;
Console.WriteLine (x-y);

// Outputs 6

El operador de división (/) divide el primer operando por el segundo. Si los operandos
son ambos enteros, cualquier resto se descarta para devolver un valor entero.
Ejemplo:
- Módulo

Elint x = 25 %de7;módulo (%) se conoce informalmente como el operador residuo


operador
Console.WriteLine (x);

// Outputs 4
porque devuelve el resto de una división entera.
Por ejemplo:
- Precedencia del operador

La precedencia del operador determina la agrupación de términos en una expresión,


lo que afecta la forma en que se evalúa una expresión. Ciertos operadores tienen
mayor prioridad sobre otros; por ejemplo, el operador de multiplicación tiene mayor
prioridad que el operador de suma.
Por ejemplo:
El programa evalúa primero 3 * 2, y luego agrega el resultado a 4.

int x = 4+3*2;
Console.WriteLine (x);

// Outputs 10

Como en matemáticas, el uso de paréntesis altera la precedencia del operador.

int x = (4 + 3) *2;
Console.WriteLine (x);

// Outputs 14
Si ninguna de las expresiones está entre paréntesis, los operadores multiplicativos
(multiplicación, división, módulo) se evaluarán antes que los operadores aditivos
(suma, resta). Los operadores de igual precedencia se evalúan de izquierda a derecha.

Las operaciones entre paréntesis se realizan primero. Si hay expresiones entre


paréntesis anidadas entre sí, la expresión dentro de los paréntesis más internos se
evalúa primero.
 Operadores de asignación e incremento
- Operadores de Asignación

El operador de asignación = asigna el valor en el lado derecho del operador a la


variable en el lado izquierdo.
C # también proporciona operadores de asignación compuesta que realizan una
operación y una asignación en una declaración.
Por ejemplo:
La misma sintaxis abreviada se aplica a los operadores de multiplicación, división y

int x = 42;
x += 2; // equivalente a x = x + 2
x - = 6; // equivalente a x = x - 6

módulo.
- Operador de incremento

x *= 8; // equivalente a x = x * 8
x / = 5; // equivalente a x = x / 5
x % = 2; // equivalente a x = x % 2

x ++; // equivalente a x = x + 1
El operador de incremento se usa para aumentar el valor de un número entero en
uno, y es un operador de C # comúnmente usado.
Por ejemplo:

- Variante de Prefijo y Sufijo

int x = 10;
x++;
++x; // prefijo
Console.WriteLine
x++; // sufijo (x);

// Outputs 11

El operador de incremento se usa para aumentar el valor de un entero en uno.

El operador de incremento tiene dos formas, prefijo y sufijo


La variante de prefijo incrementa el valor y luego continúa con la expresión.
La variante de sufijo evalúa la expresión y luego realiza el incremento.
Ejemplo de prefijo:

int x = 3;
int y = ++x;

// x es 4, y es 4

int x = 3;
int y = x++;

// x es 4, y es 3

El ejemplo de prefijo incrementa el valor de x y luego lo asigna a y.


El ejemplo de sufijo asigna el valor de x a y, y luego incrementa x.

Ejemplo de sufijo:
- Operador de decremento

El operador de decremento (--) funciona de la misma manera que el operador de


incremento, pero en lugar de aumentar el valor, lo disminuye en uno.

Condicionales y bucles
--x; // prefijo
 La x--; // sufijo

declaración if-else
- La declaración if
La instrucción if es una instrucción condicional que ejecuta un bloque de código
cuando una condición es verdadera.
La forma general de la instrucción if es:

if (condición)
{
// Ejecute este código cuando la condición sea verdadera
}
static void Main(string[] args)
{
int x = 8;
int y = 3;

if (x > y)
{
Console.WriteLine("x es mayor que y");
}
}

La condición puede ser cualquier expresión que devuelva verdadero o falso.


Por ejemplo:
El código anterior evaluará la condición x> y. Si es cierto, se ejecutará el código dentro
del bloque if.
- Operadores relacionales
Utilice operadores relacionales para evaluar condiciones. Además de los operadores
menor que (<) y mayor que (>), están disponibles los siguientes operadores: 

Cuando solo hay una línea de código en el bloque if, se pueden omitir las llaves.
Por
if (a ejemplo:
== b)
{if (x> y)
Console.WriteLine("Igual");
Console.WriteLine ("x es mayor que y");
}
// Muestra Igual si el valor de a es igual al valor de b

- La cláusula else
Se puede especificar una cláusula else opcional para ejecutar un bloque de código
cuando la condición en la instrucción if se evalúa como falsa.
Sintaxis:
if (condición)
{
// declaraciones
} ejemplo:
Por
else
{
// declaraciones
}
- Declaraciones if anidadas
También puede incluir o anidar declaraciones if dentro de otra declaración if.
Por ejemplo:

int marca = 100;


if (marca < 50) {
Console.WriteLine("Has pasado");
if (marca < 50) {
Console.WriteLine("¡Perfecto!");
}
}
else {
Console.WriteLine("Falló");
}
/* Outputs
Has pasado
¡Perfecto!
*/

Puede anidar un número ilimitado de declaraciones if-else.


Por ejemplo:

- La declaración if-else if
int edad = 17;
if (edad > 14) {
if (edad > 18) {
Console.WriteLine("Adulto");
}
else {
Console.WriteLine("Adolescente");
}
}
else {
if (edad > 0) {
Console.WriteLine("Niño");
}
else {
Console.WriteLine("Algo está mal");
}
}
// Outputs "Adolescente"

Recuerde que todas las demás cláusulas deben tener declaraciones if


correspondientes.
La declaración if-else if se puede usar para decidir entre tres o más acciones.
Por ejemplo:

int x = 33;
if (x == 8) {
Console.WriteLine("El valor de x es 8");
}
else if (x == 18) {
Console.WriteLine("El valor de x es 18");
}
else if (x == 33) {
Console.WriteLine("El valor de x es 33");
}
else {
Console.WriteLine("Sin coincidencia");
}
// Outputs "El valor de x es 33"

Recuerde, que un if puede tener cero o más else if y todos deben venir antes del
último else, el cuales opcional.
Una vez que un else if tiene éxito, ninguno de las else if o else restantes serán
evaluadas.
 La declaración switch
- Switch
La declaración switch provee una forma más elegante de evaluar la igualdad de una
variable contra una lista de valores.
Cada valor se llama un case, y la variable a ser evaluada es comparada con cada caso
del switch.
Por ejemplo:

int num = 3;
switch (num) {
case 1:
Console.WriteLine("Uno");
break;
case 2:
Console.WriteLine("Dos");
break;
case 3:
Console.WriteLine("Tres");
break;
}
// Outputs "Tres"

Cada case representa un valor a ser evaluado, seguido por dos puntos y las

Una declaración switch puede incluir cualquier número de case. Sin embargo, dos
etiquetas de case no pueden contener el mismo valor constante.
La declaración break; que finaliza cada case será cubierta muy pronto.

declaraciones a ser ejecutadas si la variable coincide con ese case.

- El caso “default”
En una declaración switch, el case opcional default es ejecutado cuando ninguno de
los case anteriores coinciden.
Ejemplo:
- La declaración de descanso
int edad = 88;
switch (edad) {
case 16:
Console.WriteLine("Demasiado joven");
break;
case 42:
Console.WriteLine("Adulto");
break;
case 70:
Console.WriteLine("Mayor");
break;
default:
Console.WriteLine("El caso default");
break;
}
// Outputs "El caso default"
El papel de la declaración break es finalizar la declaración switch.
Sin ello, la ejecución continúa más allá de las declaraciones del case que coincidió y
pasará a través de las declaraciones de los cases siguientes, aun cuando la etiqueta
del case no coincida con la variable del switch.
Este comportamiento se llama fallthrough (caer a través) y los compiladores
modernos de C # no compilarán dicho código. Todo código del case y del default debe
finalizar con una declaración break.

El La declaración break puede también ser utilizada para salir de un ciclo.

bucle while
- While
Un bucle while ejecuta repetidamente un bloque de código siempre y cuando una
condición sea verdadera.
Por ejemplo, el siguiente código despliega los números del 1 al 5:

int num = 1;
while (num < 6) {
Console.WriteLine(num);
num++;
}
/* Outputs
1
2
3
4
5
*/

El ejemplo anterior declara una variable igual a 1 (int num = 1). El bucle while valida la
condición (num <6) y, si verdadero, ejecuta las declaraciones en su cuerpo, las cuales
incrementan el valor de num en uno, antes de validar la condición del bucle otra vez.
Después de la quinta iteración, num es igual a 6, la condición se evalúa como falsa y el
ciclo deja de ejecutarse.
Los operadores aritméticos compuestos se pueden usar para controlar aún más la

El cuerpo del bucle es el bloque de declaraciones dentro de las llaves.

cantidad de veces que se ejecuta un ciclo. Por ejemplo:

int num = 1;
while (num < 6) {
Console.WriteLine(num);
num+=2;
}
/* Outputs
1
3
5
*/
Sin una declaración que eventualmente evalúe la condición del bucle como falsa, el
bucle continuará indefinidamente.

Podemos acortar el ejemplo anterior, incrementando el valor de num justo en la


condición:

int num = 1;
while (++num < 6)
Console.WriteLine(num);

¿Qué piensas, habrá diferencia entre while (num ++ <6) y while (++ num <6)?
¡Por supuesto! El bucle while (++ num <6) se ejecutará 5 veces, porque el pre-
incremento aumenta el valor de “x” antes de verificar la condición num <6, mientras
que el pos-incremento verificará la condición antes de aumentar el valor de num,
haciendo que while (num ++ <6) se ejecute 6 veces.

 El bucle for
Un bucle for ejecuta un conjunto de instrucciones un número específico de veces y tiene
la sintaxis:

for (inicio; condición; incremento) {


Instrucción(es)
}

Un contador se declara una vez en inicio.


Luego, la condición evalúa el valor del contador y el cuerpo del bucle se ejecuta si la
condición es verdadera.
Después de la ejecución del bucle, la instrucción de incremento actualiza el contador,
también llamado variable de control del bucle.
La condición se evalúa nuevamente y el cuerpo del bucle se repite, solo parando cuando
la condición se vuelve falsa.
Por ejemplo:

for (int x = 10; x < 15; x++) {


Console.WriteLine("El valor de x: {0}", x);
}
/*
El valor de x: 10
El valor de x: 11
El valor de x: 12
El valor de x: 13
El valor de x: 14
*/

Tenga en cuenta los puntos y comas en la sintaxis.


Los operadores aritméticos compuestos se pueden usar para controlar aún más las
iteraciones de bucle.
Por ejemplo:

for (int x = 0; x < 10; x+=3) {


Console.WriteLine(x);
}
/* Outputs
0
3
6
9
*/

for (int x = 10; x > 0; x-=2) {


Console.WriteLine(x);
}
/* Outputs
10
8
6
4
2
*/

También puede disminuir el contador:


Las instrucciones inicio e incremento pueden omitirse, si no es necesario, pero recuerde
que los puntos y comas son obligatorios.
Por ejemplo, el inicio se puede omitir:
Puede tener la declaración de incremento en el cuerpo del bucle for:
 El int
bucle
x = do-while
10;
for ( ; x > 0; x-=3) {
Console.WriteLine(x);
}

Un do-while bucle es similar a un bucle while, con la excepción de que un bucle do-while
está garantizado paraintejecutarse
x = 10; al menos una vez.
Por ejemplo: for ( ; x > 0; ) {
Console.WriteLine(x);
x-=3;
int a = 0; }
do {
Console.WriteLine(a);
a++; for ( ; ; ) { } es un bucle infinito.
} while (a < 5);
- /*do-while
Outputs vs. While
0 Si la condición del ciclo do- while se evalúa como falsa, las instrucciones en el do
1 todavía se ejecutarán una vez:
2
3
4
*/
 break y contine
- break
Vimos el uso de break en la declaración switch.
Otro uso de break está en los bucles: cuando la instrucción break se encuentra dentro
de un bucle, el bucle finaliza inmediatamente y la ejecución del programa pasa a la
siguiente instrucción que sigue al cuerpo del bucle.
Por ejemplo:

int num = 0;
while {
if (num == 5)
break;

Console.WriteLine(num);
num++;
}
/* Outputs
0
1
2
3
4
*/

Si está utilizando bucles anidados (es decir, un bucle dentro de otro bucle), la
instrucción break detendrá la ejecución del bucle más interno y comenzará a ejecutar
la siguiente línea de código después del bloque.

- continue
La instrucción continue es similar a la instrucción break, pero en lugar de terminar el
bucle por completo, omite la iteración actual del bucle y continúa con la siguiente
iteración.
Por ejemplo:

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


if (num == 5)
continue;

Console.WriteLine(i);
}
/* Outputs
0
1
2
3
4
6
7
8
9
*/

Como puede ver, el número 5 no se imprime, ya que la declaración continue salta las
declaraciones siguientes de esa iteración del bucle.
 Operadores lógicos
Los operadores lógicos se utilizan para unir múltiples expresiones y devolver verdadero o
falso.

- El operador AND
El operador AND (&&) funciona de la siguiente manera:
Por ejemplo, si desea mostrar texto en la pantalla solo si la age es mayor de 18 años y

money es mayor de 100:


El operador AND se usó para combinar las dos expresiones.

int age = 42;


double money=540;
if (age > 18 && money > 100) {
Console.WriteLine(“Bienvenido”);
}

Con el operador AND, ambos operandos deben ser verdaderos para que toda la
expresión sea verdadera.

Puedes unir más de dos condiciones:

int age = 42;


int grande = 75;
if (age > 16 && age < 80 && grande > 50) {
Console.WriteLine(“Hola”);
}

La expresión completa se evalúa como verdadera solo si todas las condiciones son
verdaderas.
- El operador OR
El operador OR (||) devuelve verdadero si alguno de sus operandos es verdadero.

Por ejemplo:
- El operador NOT
int age = 42;
int score = 85;
if (age > 20 || score > 50) {
Console.WriteLine(“Bienvenido”);
}

Puede unir cualquier cantidad de sentencias lógicas OR que desee.


Además, se pueden unir varias instrucciones OR y AND.

int age = 8;
if (! (age >= 16)) {
Console.WriteLine(“Tu edad es menor a 16”);
}
// Outputs “Tu edad es menor a 16”

El operador lógico NOT (!) funciona con un solo operando, invirtiendo su estado
lógico. Por lo tanto, si una condición es verdadera, el operador NOT la hace falsa, y
viceversa.

 El operador condicional (?:)


Considere el siguiente ejemplo:

int age = 8;
string msg;
if (age >= 18)
msg = “Bienvenido”;
else
msg = “Lo siento”;

Console.WriteLine(msg);
El código anterior verifica el valor de la variable de age y muestra el mensaje
correspondiente en la pantalla.
Esto se puede hacer de una manera más elegante y más corta usando el operador ?:, el
cual tiene la siguiente forma:

Exp1 ? Exp2 : Exp3;

El operador?: funciona de la siguiente manera: se evalúa Exp1. Si es cierto, entonces Exp2


se evalúa y se convierte en el valor de toda la expresión. Si Exp1 es falso, se evalúa Exp3 y
su valor se convierte en el valor de la expresión.
Entonces, el ejemplo anterior se puede reemplazar por lo siguiente:
 Calculadora básica
int age = 8;
string msg;
msg = (age >= 18) ? “Bienvenido” : “Lo siento”;
Console.WriteLine(msg);

Ahora creemos un proyecto simple que le pide repetidamente al usuario que ingrese dos
valores y luego muestra su suma, hasta que el usuario ingrese “exit”.
Comenzamos con un ciclo do- while que le pide al usuario una entrada y calcula la suma:
Este código pedirá la entrada del usuario infinitamente. Ahora tenemos que manejar el
do {
Console.WriteLine(“x =”);
int x = Convert.ToInt32 (Console.ReadLine());

Console.WriteLine(“y =”);
int y = Convert.ToInt32 (Console.ReadLine());

int sum = x+y;


Console.WriteLine(“Resultado: {0}”, sum);
} while (true);

"exit".
Si el usuario ingresa "exit" como el valor de x, el programa debe salir del bucle. Para hacer

Si el usuario ingresa un número no entero, el programa se bloqueará por un error de


conversión. Aprenderemos a manejar errores como ese en los próximos contenidos.
Console.WriteLine(“x =”);
string str = Console.ReadLine();
if (str == “exit”)
break;

int x = Convert.ToInt32 (str);

esto, podemos usar una instrucción break:


Aquí comparamos la entrada con el valor "exit" y rompemos el bucle.
do {
Console.WriteLine(“x =”);
string str = Console.ReadLine();
if (str == “exit”)
break;

int x = Convert.ToInt32 (str);

Console.WriteLine(“y =”);
int y = Convert.ToInt32 (Console.ReadLine());

int sum = x+y;


Console.WriteLine(“Resultado: {0}”, sum);
} while (true);

Entonces todo el programa se ve así:

Métodos
 Introducción a los métodos
- ¿Qué es un método?
Un método es un grupo de declaraciones que realizan una tarea particular.
Además de los métodos integrados de C #, también puede definir los suyos propios.
Los métodos tienen muchas ventajas, que incluyen:
- Código reutilizable.
- Fácil de probar.
- Las modificaciones a un método no afectan el programa de llamada.
- Un método puede aceptar muchas entradas diferentes.
- Declarando métodos

Cada programa válido de C # tiene al menos un método, el método Main.

Para usar un método, debe declarar el método y luego invocarlo.


Cada declaración de método incluye:
- el tipo de retorno
- el nombre del método
- una lista opcional de parámetros.

<return type> name (type1 par1, type2 par2, …, typeN parN) {


Lista de declaraciones
}

int Sqr (int x) {


int resultado = x*x;
return resultado;
}
Si el usuario ingresa "exit" como el valor de x, el programa debe salir del bucle.
Por ejemplo, el siguiente método tiene un parámetro int y devuelve el número al
cuadrado:
El tipo de retorno de un método se declara antes de su nombre. En el ejemplo
anterior, el tipo de retorno es int, lo que indica que el método devuelve un valor
entero. Cuando un método devuelve un valor, debe incluir una declaración return.
Los métodos que devuelven un valor a menudo se usan en las declaraciones de
asignación.
Ocasionalmente, un método realiza las operaciones deseadas sin devolver un valor.
Dichos métodos tienen un tipo de retorno void. En este caso, el método no se puede
invocar como parte de una declaración de asignación.

void es un tipo de datos básico que define un estado sin valor.

- -
Invocando métodos
Los parámetros son opcionales; es decir, puede tener un método sin parámetros.
Como ejemplo, definamos un método que no retorne un valor y solo imprime una
línea de texto en la pantalla.

static void SayHi () {


Console.WriteLine(“Hello”);
}

Nuestro método, titulado SayHi, retorna void y no tiene parámetros.


Para ejecutar un método, simplemente llame al método utilizando el nombre y
cualquier argumento requerido en una declaración.

static void SayHi () {


Console.WriteLine(“Hello”);
}

static void Main (string [] args) {


SayHi ();
}

La palabra clave static se discutirá más adelante; se usa para hacer que los métodos
sean accesibles en Main.

Puede llamar al mismo método varias veces:


 static void SayHi () {
Console.WriteLine(“Hello”);
}

static void Main (string [] args) {


SayHi ();
SayHi ();
SayHi ();
}
/*Outputs:
Hello
Hello
Hello
*/

Parámetros de métodos
- Parámetros
Las declaraciones de métodos pueden definir una lista de parámetros para trabajar.
Los parámetros son variables que aceptan los valores pasados al método cuando se
les llama.
Por ejemplo:

void Print (int x) {


Console.WriteLine(x);
}

Los parámetros se comportan dentro del método de manera similar que otras variables
locales. Se crean al ingresar al método y se destruyen al salir del método.

Esto define un método que toma un parámetro entero y muestra su valor.


Ahora puede llamar al método en Main y pasar el valor de sus parámetros (también
llamados argumentos):
Puede pasar diferentes argumentos al mismo método siempre que sean del tipo
esperado.
Por ejemplo:

static void Print (int x) {


Console.WriteLine(x);
}
static void Main (string [] args) {
Print (42);
}

El valor 42 se pasa al método como argumento y se asigna al parámetro formal “x”.


 static void Func (int x) {
Console.WriteLine(x*2);
}
static void Main (string [] args) {
Func (5);
// Output 10

Func (12);
// Output 24

Func (42);
// Output 84
}

Múltiples parámetros
Puede tener tantos parámetros como sea necesario para un método separándolos con
comas en la definición.
Creemos un método simple que devuelva la suma de dos parámetros:

int Sum (int x, int y) {


return x+y;
}

El método Sum toma dos enteros y devuelve su suma. Es por esto que el tipo de retorno

Los métodos retornan valores utilizando la declaración return.

del método es int. El tipo de datos y el nombre deben definirse para cada parámetro.
Un método invocado con múltiples parámetros debe separar los argumentos con comas.
Por ejemplo, el invocar Sum requiere dos argumentos:
En el código anterior, el valor de retorno se mostró en la ventana de la consola.

static void Main (string [] args) {


Console.WriteLine (Sum (8, 6));
// Output 14
}

Alternativamente, podemos asignar el valor de retorno a una variable, como en el


siguiente código:

static void Main (string [] args) {


int res = Sum (11, 42);
 Argumentos opcionales y con(res);
Console.WriteLine nombres
// Output 53
}

Puede agregar tantos parámetros a un solo método como desee. Si tiene varios
parámetros, recuerde separarlos con comas, tanto al declararlos como al llamar al
método.
Cuando definimos un método, puedes especificar un valor predeterminado para
parámetros opcionales. Fíjate que los parámetros opcionales deben ser definidos
después que los parámetros requeridos. Si los argumentos correspondientes son
omitidos cuando es invocado el método, el método utiliza los valores
predeterminados.
Para hacer esto, asigna valores a los parámetros en la definición del método, tal y
como se muestra en este ejemplo.

static int Pow (int x, int y=2) {


int resultado = 1;
for (int I = 0, I < y, i++) {
resultado *= x;
}
return resultado;
}

El método Pow asigna un valor predeterminado de 2 al parámetro “y”. Si invocamos al


método sin pasar el valor para el parámetro “y”, el valor predeterminado será
utilizado.
- Argumentos con nombres
static void Main (string [] args) {
Console.WriteLine (Pow (6));
// Output 36

Console.WriteLine (Pow (3,4));


// Output 81
}

Como puedes ver, los valores de los parámetros predeterminados pueden ser
utilizados para invocar el mismo método en diferentes situaciones sin requerir
argumentos para cada parámetro.
Sólo recuerda que debes tener los parámetros con valores predeterminados al final de
la lista de parámetros cuando definas el método.

Los argumentos con nombre te liberan de la necesidad de recordar el orden de los


parámetros al invocar un método. Cada argumento puede ser especificado por la
coincidencia con el nombre del parámetro.
Por ejemplo, el siguiente método calcula el área de un rectángulo por su altura y
ancho:
Al llamar el método, puedes utilizar los nombres de los parámetros para proveer los

static int Area (int h, int w) {


return h*w;
}

argumentos en el orden que lo desees:


 static void Main (string [] args) {
int res = Area (w: 5, h: 8);
Console.WriteLine (res);
// Output 40
}

Los argumentos con nombre utilizan el nombre del parámetro seguido por dos puntos
y el valor.

Pasando argumentos
Hay tres maneras de pasar argumentos a un método cuando el método es invocado: por
valor, por referencia y como Salida.
Por valor copia el valor del argumento en el parámetro formal del método. Aquí,
podemos hacer cambios al parámetro dentro del método sin tener ningún efecto en el
argumento.
El siguiente ejemplo demuestra el método por valor:

Por defecto, C# utiliza el método por valor para pasar argumentos.

static void Sqr (int x) {


x = x * x;
}
static void Main () {
int a = 3;
Sqr (a);
Console.WriteLine (a);
// Output 3
}

Como puedes ver, el método Sqr no modifica el valor original de la variable, ya que es
pasado por valor, significando que opera sobre el valor, no sobre la variable actual.

En este caso, "x" es el parámetro del método Sqr y "a" es el verdadero argumento pasado
al método.
- Pasando por referencia
Pasando por referencia copia la dirección de memoria de un argumento en el
parámetro formal. Dentro del método, la dirección es utilizada para acceder el
argumento que se usó para invocar el método. Esto significa que los cambios hechos
al parámetro afectan al argumento.
Para pasar el valor por referencia, la palabra clave ref es utilizada tanto al invocar la
función como en la definición del método:
static void Sqr (ref int x) {
x = x * x;
}
static void Main () {
int a = 3;
Sqr (ref a);
Console.WriteLine (a);
// Output 9
}

La palabra clave ref pasa la dirección de memoria al parámetro del método, lo cual

La palabra clave ref es utilizada tanto cuando se define el método y cuando se invoca.

permite al método opera sobre la propia variable.


- Pasando por salida
Parámetros por Salida son similares a los parámetros por referencia, excepto que
ellos transfieren datos desde el método hacia el parámetro en lugar de recibir. Estos
son definidos utilizando la palabra clave out.
La variable suplida para el parámetro de salida no requiere estar inicializada ya que el
valor no será utilizado. Los parámetros de salida son particularmente útiles cuando
necesitas retornar múltiples valores desde un método.
Por ejemplo:

static void GetValues (out int x, out int y) {


x = 5;
y = 42;
}
static void Main (string [] args) {
int a, b;
GetValues (out a, out b);
//Ahora a es igual 5, b es igual a 42
}

A diferencia del ejemplo anterior de pase por referencia, donde los 3 valores fueron
referenciados por el método, el cual cambio sus valores a 9, los parámetros de salida

Similar a la palabra clave ref, la palabra clave out es utilizada tanto cuando se define el
método y cuando se invoca.

obtienen su valor desde el método (5 y 42 en el ejemplo anterior).


 Sobrecarga de métodos
Sobrecargar métodos es cuando varios métodos tienen el mismo nombre, pero
diferentes parámetros.
Por ejemplo, puedes tener un método Print que despliegue sus parámetros a la ventana
de consola:

void Print (int x) {


Console.WriteLine (“Valor:” + a);
}

El operador + es utilizado para concatenar valores. En este caso, el valor de a es unido al


texto "Valor: ".
Este método acepta solamente un argumento entero.
El sobrecargar hará que esté disponible para otros tipos, como double:

void Print (double x) {


Console.WriteLine (“Valor:” + a);
}

Ahora, el mismo nombre del método Print funcionará tanto para enteros como para
dobles.

Cuando sobrecargas métodos, las definiciones de los métodos deben diferenciarse entre
sí por los tipos y/o el número de parámetros.
Cuando hay métodos sobrecargados, el método invocado depende de los argumentos. Un
argumento entero invocará la implementación del método que acepta un parámetro
entero. Un argumento doble invocará la implementación que acepte un parámetro doble.
Múltiples argumentos llamarán la implementación que acepte el mismo número de
argumentos.
 Recursión
static void Print (int a) {
Console.WriteLine("Value: " + a);
}
static void Print (double a) {
Console.WriteLine("Value: " + a);
}
static void Print (string label, double a) {
Console.WriteLine(label + a);
}

static void Main (string [] args) {


Print (11);
Print (4.13);
Print ("Average: ", 7.57);
}
No puedes sobrecargar declaraciones de métodos que difieren sólo en el tipo
retornado.
La siguiente declaración resulta en un error.
int PrintName (int a) {}
float PrintName (int b) {}
double PrintName (int c) {}
Un método recursivo es un método que se llama a sí mismo.
Una de las tareas clásicas que pueden ser resueltas fácilmente por recursión es el cálculo
del factorial de un número.
En matemáticas, el término factorial se refiere al producto de todos los enteros positivos
que son menores o iguales que un número entero específico no negativo (n). El factorial
de n se escribe como n!
Por ejemplo:
Como puedes ver, un factorial puede ser pensado como el cálculo repetido de num *
4! = 4 * 3 * 2 * 1 = 24

Un método recursivo es un método que se llama a sí mismo.

num-1 hasta que alcanzas 1.


Basado en esta solución, vamos a definir nuestro método:

static int Fact (int num) {


if (num == 1) {
return 1;
}
return num * Fact (num - 1);
}

En el método recursivo Fact, la declaración if define la condición de salida, un caso base


que no requiera recursión. En este caso, cuando num sea igual que uno, la solución
simplemente retorna 1(el factorial de uno es uno).
La invocación recursiva es colocada después de la condición de salida y retorna num
multiplicado por el factorial de n-1.
Por ejemplo, si invocas el método Fact con un argumento igual a 4, será ejecutado de la
siguiente manera:
return 4*Fact(3), lo cual es equivalente a 4*3*Fact(2), lo cual es equivalente a
4*3*2*Fact(1), lo cual es equivalente a 4*3*2*1.
Ahora podemos invocar nuestro método Fact desde Main:
 Haciendo una pirámide
static void Main (string [] args) {
Console.WriteLine(Fact (6));
//Outputs 720
}

El método factorial se invoca a sí mismo, y luego continúa haciéndolo, hasta que el


argumento sea igual a 1. La condición de salida previene que el método se llame a si
mismo indefinidamente.

Ahora, vamos a crear un método que muestre una pirámide de cualquier altura en la
ventana de consola utilizando asteriscos (*)
Basados en esta descripción, un parámetro será definido para reflejar el número de filas
de la pirámide.
Por lo tanto, vamos a comenzar declarando el método:
static void DrawPyramid (int n) {
//El Código irá aquí
}
DrawPyramid no necesita retornar un valor y toma un parámetro entero n.
En programación, la lógica paso a paso requerida para la solución de un problema es
llamada un algoritmo. EL algoritmo para MakePyramid es:
1. La primera fila debe contener una estrella en la parte superior central de la pirámide.
El centro es calculado basado en el numero de filas en la pirámide.
2. Cada fila después de la primera debe contener un numero impar de asteriscos (1,3,5,
etc.), hasta que el número de filas es alcanzado.
Basados en el algoritmo, el código utilizara bucles for para mostrar espacios y estrellas
para cada fila:

static void DrawPyramid (int n) {


for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
Console.Write(“ “);
}
for (int k = 1; k <= 2 * i - 1; k++) {
Console.Write ("*" + " ");
}
Console.WriteLine();
}
}

El primer bucle for que itera a través de cada fila de la pirámide contiene dos bucles for.
El primer bucle interno despliega los espacios requeridos antes del primer asterisco. El
segundo bucle interno despliega el número requerido de asteriscos para cada fila, el cual
es calculado basados en la fórmula (2*i-1) donde i es la fila actual.
La declaración final Console.WriteLine() mueve el cursor a la siguiente fila.

Clases y objetos
Ahora, Si invocamos el método DrawPyramid, desplegará una pirámide cuyo número
 Clases y objetos de filas que le indiquemos al método.

- Clases
Como hemos visto en los módulos previos, los tipos de datos incorporados son
utilizados para almacenar un sólo valor en una variable declarada. Por ejemplo, int x
almacena un valor entero en una variable llamada x.
En programación orientada a objetos, una clase es un tipo de dato que define un
conjunto de variables y métodos para un objeto declarado.
Por ejemplo, si fueras a crear un programa que administre cuentas bancarias, una
clase BankAccount podría ser utilizada para declarar un objeto que tendría todas las
propiedades y métodos necesarios para administrar una cuenta bancaria individual,
tales como una variable balance y métodos para Depositar y Retirar.
Una clase es como un plano de diseño. Define el tipo de dato y comportamiento para
un tipo. Una definición de clase comienza con la palabra clave class seguida por el
nombre de la clase. El cuerpo de la clase contiene los datos y acciones encerradas
dentro de llaves.

- Objetos
class BankAccount {
//variables, métodos, etc.
}
Al igual que un tipo de dato incorporado es utilizado para declarar múltiples variables,
una clase puede ser usada para declarar múltiples objetos. Como analogía, en los
preparativos para un nuevo edificio, el arquitecto diseña unos planos, los cuales son
utilizados como una base para realmente construir la estructura. Ese mismo plano
puede ser utilizado para crear varios edificios.
La programación funciona de la misma forma. Definimos (diseñamos) una clase que es
el plano para crear objetos.
En programación, el término tipo es utilizado para referirse al nombre de una clase:
Estamos creando un objeto de un tipo en particular.
Una vez que hemos escrito la clase, podemos crear objetos basados en esa clase.
Crear un objeto es llamado instanciar.

Un objeto es llamado una instancia de una clase.

Cada objeto tiene sus propias características. Así como una persona se distingue por
su nombre, edad y género, un objeto tiene su propio conjunto de valores que lo
diferencian de otro objeto del mismo tipo.
Las características de un objeto son llamadas propiedades.
Los valores de estas propiedades describen el estado actual de un objeto. Por
ejemplo, una Persona (un objeto de la clase Persona) puede ser de 30 años, masculino
y llamarse Antonio.
Los objetos no siempre representan sólo características físicas.
Por ejemplo, un objeto en programación puede representar una fecha, una hora y una
cuenta bancaria. Una cuenta bancaria no es tangible; no puedes verla o tocarla, pero
sigue siendo un objeto bien definido porque tiene sus propias propiedades.
 Tipos por valor y referenciados
- Tipos por valor
C# tiene dos formas de almacenar datos: por referencia y por valor.
Los tipos de datos incorporados, como int y double, son utilizados para declarar
variables que son tipos de valor. Su valor está almacenado en memoria, en una
ubicación llamada el stack.
Por ejemplo, la declaración y asignación int x = 10; puede ser pensada como:
- Tipos referenciados
El valor de la variable "x" está ahora almacenado en el stack.

Los tipos referenciados son utilizados para almacenar objetos. Por ejemplo, cuando
creas un objeto de una clase, es almacenado como un tipo referenciado.
Los tipos referenciados son almacenados en una parte de la memoria llamada el
heap.
Cuando instancias un objeto, los datos para ese objeto son almacenado en el heap,
mientras que la ubicación de memoria del heap es almacenada en el stack.
Es por esto que es llamado un tipo referenciado - contiene una referencia (la dirección
de memoria) al objeto actual en el heap.

Como puedes ver, el objeto p1 del tipo Person en el stack almacena la dirección de
memoria del heap donde el objeto actual es almacenado.

El Stack es utilizado para alojamiento estática en memoria, lo cual incluye todos los
tipos de valor, como "x".
El Heap es utilizado para alojamiento dinámico de memoria, lo cual incluye objetos
personalizados, que pueden necesitar memoria adicional durante la ejecución de tu
programa.

Ejemplo de clase
Vamos a crear una clase Person:

class Person {
int age;
string name;
public void SayHi () {
Console.WriteLine("Hi");
}
}

El código anterior declara una clase llamada Person, la cual tiene campos denominados
age (edad) y name (nombre) así como un método SayHi que despliega un saludo en la
pantalla.
Puedes incluir un modificador de acceso para campos y métodos (también llamados
miembros) de una clase. Los modificadores de acceso son palabras claves utilizadas para
especificar la accesibilidad de un miembro.
Un miembro que ha sido definido como público puede ser accedido desde fuera de la
clase, siempre y cuando esté en cualquier parte dentro del alcance del objeto de la clase.

También puedes designar a los miembros de clase como privados o protegidos. Esto
será discutido en mayor detalle posteriormente en el curso. Si ningún modificador de
acceso está definido, el miembro es privado por defecto.
Esta es la razón del por qué nuestro método SayHi está declarado público, ya que vamos a
invocarlo desde afuera de la clase.
Fíjate que cuando tenemos nuestra clase Person definida, podemos instanciar un objeto
de este tipo en Main.
El operador new instancia un objeto y retorna una referencia a su ubicación:

class Person {
int age;
string name;
public void SayHi () {
Console.WriteLine("Hi");
}
}
static void Main (string [] args) {
Person p1 = new Person ();
p1.SayHi ();
}
//Outputs "Hi"

El código anterior declara un objeto Person llamado p1 y luego llama a su método público

Fíjate en el operador punto (.) que es utilizado para acceder e invocar el método del
objeto.

SayHi().
Puedes acceder a todos los miembros públicos de una clase utilizando el operador punto.
Además de invocar métodos, puedes utilizar el operador punto para hacer una asignación
cuando sea válido.
Por ejemplo:
 Encapsulación
class Dog {
public string name;
public int age;
}
static void Main (string [] args) {
Dog bob = new Dog ();
bob.name = “Bobby”;
bob.age = 3;

Console.WriteLine(bob.age);
//Outputs 3
}

Parte del significado de la palabra encapsulación es la idea de "rodear" una entidad, no


sólo para mantener junto lo que está adentro, pero también para protegerlo.
En programación, la encapsulación significa más que simplemente combinar miembros
dentro de una clase; también significa restringir acceso a las funciones internas de esa
clase.
La encapsulación es implementada utilizando modificadores de acceso. Un modificador
de acceso define el alcance y visibilidad de un miembro de clase.

La encapsulación es también llamada ocultamiento de información.

C# soporta los siguientes modificadores de acceso: "public", "private", "protected",


"internal", "protected internal".
Como se ha visto en los ejemplos anteriores, el modificador de acceso "public" (público)
hace que el miembro sea accesible desde fuera de la clase.
El modificador de acceso "private" (privado) hace que los miembros sean accesibles sólo
desde dentro de la clase y los oculta del exterior.
Para mostrar la encapsulación en acción, vamos a considerar el siguiente ejemplo:
"protected" (protegido) será discutido posteriormente en el curso.

Hemos utilizado encapsulación para ocultar el miembro balance del código externo.
Luego hemos proveído class BankAccount
acceso { al mismo utilizando métodos públicos. Los
restringido
datos de clase pueden ser leídos a través delbalance=0;
private double método GetBalance y modificado sólo a
través de los métodos Deposit public void Deposit (double n) {
y Withdraw.
No puedes directamente cambiar la balancevariable += n;
balance. Sólo puedes ver su valor utilizando
}
el método público. Esto ayuda a mantener la integridad de los datos.
public void Withdraw (double n) {
Podemos añadir diferentes mecanismos de verificación y validación a los métodos para
balance -= n;
proveer seguridad adicional y prevenir errores.
}
 Constructores public double GetBalance () {
En resumen, los beneficios de la encapsulación son:
return balance;
- Controlar la manera en que } los datos son accedidos o modificados.
- El código es más flexible
} y fácil de cambiar a partir de nuevos requerimientos.
- Poder modificar una parte del código sin afectar otras partes del mismo.

Un constructor de clase en un miembro especial de una clase que es ejecutado cada vez
que un nuevo objeto d esa clase es creada.
Un constructor tiene exactamente el mismo nombre que su clase, es público y no tiene
ningún tipo de retorno.
Por ejemplo:

class Person {
private int age;
public Person () {
Console.WriteLine("Hola");
}
}

static void Main (string [] args) {


Person p = new Person ();
}
// Outputs
Esto puede"Hola"
ser útil en un diverso número de situaciones. Por ejemplo, cuando se esté
creando un objeto del tipo BankAccount, puedes enviar un correo de notificación al
dueño.
La misma funcionalidad puede ser lograda utilizando un método público separado. La
ventaja del constructor es que es invocado automáticamente.
Ahora, al momento de creación de un objeto del tipo Person, el constructor es
automáticamente invocado.
Los constructores pueden ser muy útiles para fijar los valores iniciales para ciertas
variables miembro.
Un constructor predeterminado no tiene parámetros. Sin embargo, cuando es necesario,
los parámetros pueden ser añadidos a un constructor. Esto permite asigna un valor inicial
a un objeto cuando es creado, como se muestra en el siguiente ejemplo:

class Person {
private int age;
private string name;
public Person (string nm) {
name = nm;
}
public string getName() {
return name;
}
}
static void Main (string [] args) {
Person p = new Person("David");
Console.WriteLine(p.getName());
}
//Outputs "David"

Ahora, cuando el objeto es creado, podemos pasar un parámetro que será asignado a la

Los constructores pueden ser sobrecargados como cualquier método utilizando un


número diferente de parámetros.

variable name.
 Propiedades
Como hemos visto en las lecciones anteriores, es una buena práctica encapsular los
miembros de una clase y proveer acceso a ellos sólo a través de métodos públicos.
Una propiedad es un miembro que provee un mecanismo flexible para leer, escribir o
computar el valor de un campo privado. Las propiedades pueden ser utilizadas como si
fueran miembros públicos de datos, pero realmente incluyen métodos especiales
llamados descriptores de acceso (accessors).
El descriptor de acceso de una propiedad contiene las declaraciones ejecutables que
ayudan a obtener (leer o computar) o fijar (escribir) un campo correspondiente. Las
declaraciones del descriptor de acceso pueden incluir un descriptor de acceso get, un
descriptor de acceso set, o ambas.
Por ejemplo:

class Person {
private string name; //campo
public string Name {//propiedad
get {return name;}
set {name = value;}
}
}
La clase Person tiene una propiedad Name que tiene tanto el descriptor de acceso set
como el descriptor de acceso get.
El descriptor de acceso set es utilizado para asignar un valor a la variable name; mientras
que get es utilizado para retornar su valor.

"value" es una palabra clave especial, la cual representa el valor que asignamos a una
propiedad utilizando el descriptor de acceso set.
El nombre de la propiedad puede ser cualquier cosa que desees, pero las convenciones
de codificación dictan que las propiedades tengan el mismo nombre que el campo
privado con la primera letra en mayúscula.

Una vez que la propiedad está definida, podemos utilizarla para asignar y leer el miembro

class Person {
private string name;
public string Name {
get {return name;}
set {name = value;}
}
}
static void Main (string [] args) {
Person p = new Person ();
p.Name = "Bob";
Console.WriteLine(p.Name);
}

La propiedad es accedida por su nombre, tal cual cualquier otro miembro público de la
clase.

privado:
Cualquier descriptor de acceso de una propiedad puede ser omitido.
Por ejemplo, el siguiente código crea una propiedad que es sólo lectura:

class Person {
private string name;
public string Name {
get {return name;}
}
}

Una propiedad puede también ser privada, por lo que sólo podrá ser invocada desde
dentro de la clase.

class Person {
Entonces, ¿para qué utilizamos propiedades? ¿Por qué no simplemente declaramos
private int age=0;
pública a la variable
public miembro
int Age { y la accedemos directamente?
Con propiedades tienes
get {return age;}de controlar la lógica de acceso a la variable.
la opción
Por ejemplo, puedes set {validar si el valor de age es mayor que 0, antes de asignarlo a la
variable: if (value > 0)
age = value;
}
}
}
Cuando no necesitas ninguna lógica personalizada, C# provee un mecanismo rápido y
efectivo para declarar miembros privados a través de sus propiedades.
Por ejemplo, para crear un miembro privado que sólo pueda ser accedido a través de
los descriptores de acceso get y set de la propiedad Name, utiliza la siguiente sintaxis:
Como puedes ver, no necesitas declarar el campo privado "name" por separado - es
public string Name {get; set;}

creado por la propiedad automáticamente. Name es llamada una propiedad auto


implementada. También llamadas propiedades automáticas, permiten una
declaración fácil y reducida de miembros privados.
Podemos reescribir el código de nuestro ejemplo anterior utilizando una propiedad
automática:

Arreglos y Strings
class Person {
public string Name {get; set;}
 }
static void Main (string [] args) {
Person p = new Person ();
p.Name = "Bob";
Console.WriteLine(p.Name);
}
// Outputs "Bob"

Arreglos
C# provee numerosas clases incorporadas para almacenar y manipular datos.
Un ejemplo de tales clases es la clase Array.
Un arreglo es una estructura de datos que es utilizado para almacenar una colección de
datos. Puedes imaginarlo como una colección de variables del mismo tipo.
Por ejemplo, considera una situación donde necesitas almacenar 100 números. En lugar
de declarar 100 variables diferentes, puedes simplemente declarar un arreglo que
almacene 100 elementos.
Para declarar un arreglo, especifica el tipo de sus elementos seguido por corchetes:
Esto declara un arreglo de enteros.
int [ ] myArray;

int [ ] myArray = new int[5];

Como los arreglos son objetos, necesitamos instanciarlos con la palabra clave new:
Esto instancia un arreglo llamado myArray que almacena 5 enteros.
Después de crear el arreglo, puedes asignar valores a elementos individuales utilizando el
número de índice:
Esto asignará el valor 23 al primer elemento del arreglo.
Podemos proveer valores iniciales al arreglo cuando es declarado utilizando llaves:
Podemos omitir la declaración del tamaño cuando el número de elementos es proveído

int [ ] myArray = new int[5];


string
myArray[0]
[ ] names
= 23;= new string[] {"John", "Mary", "Jessica"};
Los arreglos
double en C#
[ ] prices son indexados
= new desde
double[] {3.6, 9.8,cero, lo que significa que el primer elemento
6.4, 5.9};
tiene índice 0, el segundo 1, y así sucesivamente.
string [ ] names = new string[3] {"John", "Mary", "Jessica"};
double [ ] prices = new double[4] {3.6, 9.8, 6.4, 5.9};

son proveído en las llaves:


Incluso podemos omitir el operador new. Las siguientes declaraciones son idénticas a las
anteriores:
Como fue mencionado, cada elemento de un arreglo tiene un índice numérico.
Por ejemplo, considera el siguiente arreglo:
Los elementos de b tienen los siguientes índices:

int [ ] b = {11, 45, 62, 70, 88};


string [ ] names = {"John", "Mary", "Jessica"};
double [ ] prices = {3.6, 9.8, 6.4, 5.9};

Cualquier valor debe ser proveído en una lista separada por comas rodeada de {llaves}.

Para acceder a elementos individuales del arreglo, coloca el índice numérico del elemento
entre corchetes después del nombre del arreglo.
 Utilizando arreglos en bucles
Console.WriteLine(b [2]);
//Outputs 62

Console.WriteLine(b [3]);
//Outputs 70

Recuerda que el primer elemento tiene índice 0.

- Arreglos y bucles
Ocasionalmente es necesario iterar a través de los elementos de un arreglo, haciendo
asignación de elementos basados en ciertos cálculos. Esto puede ser logrado
fácilmente utilizando bucles.
Por ejemplo, puedes declarar un arreglo de 10 enteros y asignar a cada elemento un
valor par con el siguiente bucle:

int [ ] a = new int[10];


for (int k = 0; k < 10; k++) {
a[k] = k*2;
}
También podemos utilizar un bucle para leer los valores de un arreglo.
Por ejemplo, podemos desplegar los contenidos del arreglo que acabamos de crear:

for (int k = 0; k < 10; k++) {


Console.WriteLine(a[k]);
}

La variable k es utilizada para acceder a cada elemento del arreglo.


El último índice en el arreglo es 9, por lo que la condición del bucle for es k<10.

Esto desplegará los valores de los elementos del arreglo.


- El bucle foreach
El bucle foreach provee una manera más corta y fácil de acceder a los elementos de
arreglos.
El ejemplo anterior donde accedimos a los elementos pudo ser escrito utilizando un
bucle foreach:

foreach (int k in a) {
Console.WriteLine(k);
}

El bucle foreach itera a través del arreglo "a" y asigna el valor del elemento actual a la
variable "k" en cada iteración del bucle. Así, en la primera iteración, k=a[0], en la

La variable k es utilizada para acceder a cada elemento del arreglo.


El último índice en el arreglo es 9, por lo que la condición del bucle for es k<10.

segunda, k=a[1], etc.


El tipo de dato de la variable en el bucle foreach debe coincidir el tipo de los
elementos del arreglo.
Comúnmente, la palabra clave var es utilizada como el tipo de la variable, de esta
forma: foreach (var k in a). El compilador determina el tipo apropiado para var.
El siguiente código utiliza un bucle foreach para calcular la suma de todos los
elementos de un arreglo:

int [ ] arr = {11, 35, 62, 555, 989};


int sum = 0;

foreach (int x in arr) {


sum += x;
}

Console.WriteLine(sum);
//Outputs 1652
En resumen, declaramos un arreglo y una variable sum que almacenará la suma de los
elementos.
A continuación, utilizamos un bucle foreach para iterar a través de cada elemento del
arreglo, añadiendo el valor del elemento correspondiente a la variable sum.

La clase Array provee algunos métodos útiles que serán discutidos en las próximas
lecciones.

Arreglos multidimensionales
Un arreglo puede tener múltiples dimensiones. Un arreglo multidimensional es declarado
como sigue:

type [, , … ,] arrayName = new type[size1, size2, …, sizeN];

int [ , ] x = new int[3,4];

Por ejemplo, vamos a definir un arreglo bidimensional de 3x4 enteros:


Visualiza este arreglo como una tabla compuesta de 3 filas(rows) y 4 columnas(columns):
Podemos inicializar arreglos multidimensionales de la misma manera que los arreglos
unidimensionales.
Por ejemplo:

int [ , ] someNums = { {2, 3}, {5, 6}, {4, 6} };

La indexación de arrays empieza a partir de 0.

Esto creará un arreglo con tres filas y dos columnas. Llaves anidadas son utilizadas para
definir valores para cada fila.
Para acceder un elemento del arreglo, provee ambos índices. Por ejemplo: someNums [2,
0]int [ , ] x = new
retornará int[3,4];
el valor 4, ya que accede a la primera columna de la tercera fila.
for (int k = 0; k < 3;
Vamos a crear un programa k++) { que desplegará los valores del arreglo en la forma de una
for (int j = 0; j < 2; j++) {
tabla.
Console.Write(someNums [k, j] +" ");
}
Console.WriteLine();
}
Hemos utilizado dos bucles for anidados, uno para iterar a través de las filas y otro a
través de las columnas.
La declaración Console.WriteLine(); moviliza la salida a una nueva línea después de que
cada fila es impresa.
 Jagged Arrays
Los arreglos pueden tener cualquier número de dimensiones, pero mantén presente
que los arreglos con más de tres dimensiones son más difíciles de administrar.

Un jagged array es un arreglo cuyos elementos son arreglos. Básicamente es un arreglo


de arreglos.
La siguiente es una declaración de un arreglo unidimensional que tiene tres elementos,
cada uno es un arreglo unidimensional de enteros:

int [ ][ ] jaggedArr = new int[3][ ];

Cada dimensión es un arreglo, por lo que también puedes inicializar el arreglo durante la

int [ ][ ] jaggedArr = new int[ ][ ]


{
new int [ ] {1,8,2,7,9},
new int [ ] {2,4,6},
new int [ ] {33,42}
};

declaración de la siguiente forma:


Puedes acceder elementos individuales del arreglo como se muestra en el ejemplo
siguiente:
Esto accede al segundo elemento del tercer arreglo.
 Propiedades de arreglos y métodos
- Propiedades de arreglos
La clase Array en C# provee varias propiedades y métodos para trabajar con arreglos.

Un jagged array es un arreglo de arreglos, por lo que un int[ ][ ]


es un arreglo de int[ ], cada uno de los cuales puede ser de diferentes longitudes y
ocupar su propio bloque en memoria.
Un arreglo multidimensional(int[,]) es un bloque sencillo de memoria (esencialmente
una int [ ] arr Siempre
matriz). = {2, 4, 7};
tendrá
//42 la misma cantidad de columnas para cada fila.
int xConsole.WriteLine(arr.Length);
= jaggedArr[2][1];
//Outputs 3

Console.WriteLine(arr.Rank);
//Outputs 1

Por ejemplo, las propiedades Length y Rank retornan el número de elementos y el


número de dimensiones del arreglo, respectivamente. Puedes accederlas utilizado la
sintaxis de punto, igual que cualquier miembro de clase:
La propiedad Length puede ser útil en bucles for donde necesitas especificar el
número de veces que el bucle debe ejecutarse.
Por ejemplo:
- Métodos de Array
int [ ] arr = {2, 4, 7};
for (int k=0; k<arr.Length; k++) {
Console.WriteLine(arr[k]);
}

Hay varios métodos disponibles para arreglos.


Max retorna el elemento de mayor valor.
Min retorna el elemento de menor valor.
Sum retorna la suma de todos los elementos.
Por ejemplo:

int [ ] arr = { 2, 4, 7, 1};


Console.WriteLine(arr.Max());
//Outputs 7

Console.WriteLine(arr.Min());
//Outputs 1

Console.WriteLine(arr.Sum());
//Outputs 14

C# también provee una clase Array estática con métodos adicionales. Aprenderás
acerca de estas en el siguiente módulo.
 Trabajando con strings
- Strings
Es común pensar en strings como arreglos de caracteres. En realidad, strings en C#
son objetos.
Cuando declaras una variable string, básicamente estás instanciando un objeto del
tipo String.
Los objetos String soportan un número de propiedades y métodos útiles:
Length retorna la longitud del string.
IndexOf(valor) retorna el índice de la primera ocurrencia del valor dentro del string.
Insert (índice, valor) inserta el valor dentro del string comenzando desde el índice
especificado.
Remove(índice) remueve todos los caracteres en el string después del índice
especificado.
Replace(viejoValor, nuevoValor) reemplaza el valor especificado en el string.
Substring (índice, longitud) retorna un substring de la longitud especificada,
comenzando desde el índice especificado. Si la longitud no es especificada, la
operación continúa hasta el final del string.
Contains(valor) retorna true (verdadero) si el string contiene el valor especificado.
Los siguientes ejemplos demuestran cada uno de los miembros de String:
string a = "some text";
Console.WriteLine(a.Length);
//Outputs 9

Console.WriteLine(a.IndexOf('t'));
//Outputs 5

a = a.Insert(0, "This is ");


Console.WriteLine(a);
//Outputs "This is some text"

a = a.Replace("This is", "I am");


Console.WriteLine(a);
//Outputs "I am some text"

if(a.Contains("some"))
Console.WriteLine("found");
//Outputs "found"

a = a.Remove(4);
Console.WriteLine(a);
//Outputs "I am"

a = a.Substring(2);
Console.WriteLine(a);
//Outputs "am"

También puedes acceder caracteres de una string por su índice, tal como se accede a
los elementos de un arreglo:
- Trabajando con Strings

string a = "some text";


Console.WriteLine(a [2]);
//Outputs "m"
string text = "This is some text about a dog. The word dog appears in this text a number
of times. This is the end.";

text = text.Replace("dog", "cat");


text = text.Substring(0, text.IndexOf(".")+1);

Los índices en strings son similares a los arreglos, ellos comienzan desde 0.
Console.WriteLine(text);
//Outputs: "This is some text about a cat."

Vamos a crear un- programa que tome un string, reemplace todas las ocurrencias de
la palabra "dog" con "cat" y tenga como salida sólo la primera oración.
El código anterior reemplaza todas las ocurrencias de "dog" con "cat". Después de
eso, toma un substring del string original comenzando desde el primer índice hasta la
primera ocurrencia de un carácter punto.
Hemos sumado uno al índice del punto para incluir el punto en el substring.

C# provee una colección sólida de herramientas y métodos para trabajar y manipular


strings. Puedes, por ejemplo, encontrar el número de veces que una palabra específica
aparece en un libro con facilidad, utilizando estos métodos.
Más acerca de clases
 Destructores
Así como los constructores son utilizados cuando una clase es instanciada, los
destructores son invocados automáticamente cuando un objeto es destruido o eliminado.
Los destructores tienen los siguientes atributos:
- Una clase sólo puede tener un destructor.
- Los destructores no pueden ser invocados. Son invocados automáticamente.
- Un destructor no toma modificadores ni tiene parámetros.
- El nombre de un destructor es exactamente el mismo que el de la clase con una tilde de
la ñ (~) al comienzo.
Por ejemplo:

class Dog {
~Dog () {
// code statements
}
}

Los destructores pueden ser muy útiles para liberar recursos antes de salir del
programa. Esto puede incluir cerrar archivos, liberar memoria y similares.

Vamos a incluir declaraciones WriteLine en el destructor y constructor de nuestra clase y

class Dog {
public Dog () {
Console.WriteLine("Constructor");
}
~Dog () {
Console.WriteLine("Destructor");
}
}
static void Main (string [] args) {
Dog d = new Dog ();
}
/*Outputs:
Constructor
Destructor
/*

ver cómo el programa se comporta cuando un objeto de esa clase es creado y cuando el
programa finaliza:
Cuando el programa se ejecuta, primero crea el objeto, el cual llama al constructor. El
objeto es eliminado al final del programa y el destructor es invocado cuando la ejecución
del programa está completa.
 Miembros estáticos
- Static
Ahora es tiempo de discutir la palabra clave static.
La viste primera en la declaración del método Main:

static void Main (string [] args)


Los miembros de clases (variables, propiedades, métodos) también pueden ser
declarados como estáticos. Esto hace que esos miembros pertenezcan a la clase
misma, en lugar de pertenecer a objetos individuales. No importa cuántos objetos de
la clase sean creados, sólo habrá una copia del miembro estático.
Por ejemplo:

class Cat {
public static int count=0;
public Cat () {
count++;
}
}

En este caso, hemos declarado una variable miembro pública count, la cual es
estática. El constructor de la clase incrementa la variable count en uno.

Sin importar cuántos objetos Cat sean instanciados, siempre habrá una sola variable
count que pertenece a la clase Cat porque fue declarada estática.

Debido a su naturaleza global, los miembros estáticos pueden ser accedidos


directamente utilizando el nombre de la clase sin necesidad de un objeto.
Por ejemplo:

class Cat {
public static int count=0;
public Cat () {
count++;
}
}
static void Main (string [] args) {
Cat c1 = new Cat ();
Cat c2 = new Cat ();

Console.WriteLine(Cat.count);
}
//Outputs 2

Como puedes ver, podemos acceder a la variable estática utilizando el nombre de


clase: Cat.count.
La variable count es compartida entre todos los objetos Cat. Para esta clase, cada vez
que un objeto es creado, el valor estático es incrementado. El programa anterior
demuestra esto cuando despliega 2 después de crear dos objetos de esa clase.
- Métodos estáticos

Debes acceder a los miembros estáticos utilizando el nombre de la clase. Si tratas de


class Dog { a través de un objeto de esa clase, generarás un error.
accederlos
public static void Bark () {
Console.WriteLine("Woof");
El mismo }concepto aplica a métodos estáticos.
} ejemplo:
Por
static void Main (string [] args) {
Dog.Bark();
}
// Outputs "Woof"
Los métodos estáticos pueden acceder sólo miembros estáticos.
- Static
Los miembros constantes son estáticos por definición.
Por ejemplo:

class MathClass {
public const int ONE = 1;
}
static void Main (string [] args) {
Console.Write(MathClass.ONE);
}
//Outputs 1
El método Main es estático, ya que es el punto de partida de cualquier programa. Por
lo tanto, cualquier método invocado directamente desde Main tiene que ser estático.

Como puedes ver, accedimos a la propiedad ONE utilizando el nombre de la clase, al


igual que un miembro estático. Esto es porque todos los miembros constantes son
estáticos por defecto.
- Constructores estáticos
Los constructores pueden ser declarados estáticos para inicializar miembros estáticos
de la clase.
El constructor estático es invocado automáticamente una vez cuando accedemos un
miembro estático de la clase.
Por ejemplo:

El constructor
class SomeClassserá
{ invocado una vez cuando tratemos de acceder SomeClass.X o
SomeClass.Y.
public static int X {get; set;}
public static int Y {get; set;}

static SomeClass () {
X = 10;
Y = 20;
}
}

Clases estáticas
Una clase entera puede ser declarada como estática.
Una clase estática puede contener sólo miembros estáticos.
No puedes instanciar un objeto de una clase estática, ya que sólo una instancia de la clase
estática puede existir en un programa.
Las clases estáticas son útiles para combinar propiedades lógicas y métodos. Un buen
ejemplo de esto es la clase Math.
Contiene varias propiedades y métodos útiles para operaciones matemáticas.
Por ejemplo, el método Pow eleva un número a una potencia:

Console.WriteLine(Math.Pow(2, 3));
//Outputs 8
Accedes a todos los miembros de la clase Math utilizando el nombre de la clase, sin
declarar un objeto.
Hay una cantidad de métodos y propiedades estáticas útiles disponibles en C#:
- Math
Math.PI la constante PI.
Math.E representa el logaritmo natural base e.
Math.Max() retorna el mayor de sus dos argumentos.
Math.Min() retorna el menor de sus dos argumentos.
Math.Abs() retorna el valor absoluto de su argumento.
Math.Sin() retorna el seno del ángulo especificado.
Math.Cos() retorna el coseno del ángulo especificado.
Math.Pow() retorna la potencia especificada del número especificado.
Math.Round() redondea el número decimal al valor entero más cercano.
Math.Sqrt() retorna la raíz cuadrada de un número especificado.
- Array
La clase Array incluye algunos métodos estáticos para manipular arreglos:
- String
- DateTime
Int [] arr = {1, 2, 3, 4};

Array.Reverse(arr);
//arr = {4, 3, 2, 1}

Array.Sort(arr);
//arr = {1, 2, 3, 4}

string s1 = "some text";


string s2 = "another text";

String.Concat(s1, s2); // combines the two strings

String.Equals(s1, s2); // returns false

La estructura DateTime te permite trabajar con fechas.


 This y readonly
- La palabra clave this
DateTime.Now; // represents the current date & time
DateTime.Today; // represents the current day

DateTime.DaysInMonth(2016, 2);
//return the number of days in the specified month

La clase Console es también un ejemplo de una clase estática. Utilizamos su método


estático WriteLine () para imprimir en la pantalla, o el método estático ReadLine () para
obtener entrada del usuario.
La clase Convert utilizada para convertir tipos de valores es también una clase estática.

La palabra clave this es utilizada dentro de la clase y se refiere a la instancia actual de


la clase, lo que significa que se refiere al objeto actual.
Uno de los usos más comunes de this es para distinguir miembros de clase de otros
datos, como parámetros locales o formales de un método, tal y como se muestra en
el siguiente ejemplo:

class Person {
private string name;
public Person (string name) {
this.name = name;
}
}

Aquí, this.name representa el miembro de la clase, mientras que name representa el


parámetro del constructor.
- El modificador readonly
Otro uso común del this es para pasar la instancia actual a un método como
parámetro: ShowPersonInfo(this);

El modificador readonly previene a un miembro de una clase de ser modificado


después de su construcción. Esto significa que el campo declarado como readonly
puede ser modificado solamente cuando lo declaras o dentro de un constructor.
Por ejemplo:

class Person {
private readonly string name = "John";
public Person (string name) {
this.name = name;
}
}

Si tratamos de modificar el campo name en cualquier otra parte, tendremos un error.


Hay tres grandes diferencias entre utilizar readonly y const.
Primero, un campo constante debe ser inicializado cuando es declarado, mientras que
un campo readonly puede ser declarado sin ser inicializado, como en:

readonly string name; // OK


const double PI; // Error

Segundo, un campo readonly puede ser modificado en un constructor, pero un valor


constante no.
Tercero, al campo readonly le puede ser asignado un valor que sea un resultado de un
cálculo, pero a un campo constante no, como en:

readonly double a = Math.Sin(60); // OK


const double b = Math.Sin(60); // Error!

El modificador readonly previene a un miembro de una clase de ser modificado


después de su construcción.
 Indexadores
Un indexador permite que los objetos sean indexados como un arreglo.
Tal y como fue discutido anteriormente, una variable string es en realidad un objeto de la
clase String. Más aún, la clase String es en realidad un arreglo de objetos Char. De esta
manera, la clase String implementa un indexador para que podamos acceder a cualquier
carácter (Char object) por su índice:

string str = "Hello World";


char x = str [4];
Console.WriteLine(x);
//Outputs "o"

Los arreglos utilizan índices enteros, pero los indexadores pueden utilizar cualquier tipo
de índice, como strings, caracteres, etc.

La declaración de un indexador es en cierta forma similar a una propiedad. La diferencia


está en que los descriptores de acceso del indexador requieren un índice.
Como una propiedad, utilizas descriptores de acceso get y set para definir un indexador.
Sin embargo, donde las propiedades retornan o fijan un miembro de dato específico, los
indexadores retornan o fijan un valor particular de la instancia del objeto.
Los indexadores son definidos con la palabra clave this.
Por ejemplo:

class Clients {
private string [] names = new string [10];

public string this [int index] {


get {
return names[index];
}
set {
names[index] = value;
}
}
}

Cómo puedes ver, la definición del indexador incluye la palabra clave this y un índice, el
cual es utilizado para obtener y fijar el valor apropiado.
Ahora, cuando declaramos un objeto de la clase Clients, utilizamos un índice para
referirnos a objetos específicos como los elementos de un arreglo:
 Sobrecarga de operador
Clients c = new Clients ();
c [0] = "Dave";
c [1] = "Bob";

Console.WriteLine(c [1]);
//Outputs "Bob"

Normalmente utilizas un indexador si la clase representa una lista, colección o arreglo


de objetos.
La mayoría de los operadores en C# pueden ser sobrecargados, lo que significa que
pueden ser redefinidos para acciones personalizadas.
Por ejemplo, puedes redefinir la acción del operador suma (+) en una clase personalizada.
Considera la clase Box que tiene las propiedades Height y Width:

class Box {
public int Height {get; set;}
public int Width {get; set;}
public Box (int h, int w) {
Height = h;
Width = w;
}
}
static void Main (string [] args) {
Box b1 = new Box (14, 3);
Box b2 = new Box (5, 7);
}

Quisiéramos sumar estos dos objetos Box, lo cual resultará en un nuevo y más grande
objeto Box.
Así que, básicamente, queremos que el siguiente código funcione:

Box b3 = b1 + b2;

Las propiedades Height y Width del objeto b3 deben ser igual que la suma de las

Esto es logrado a través de la sobrecarga de operadores.

propiedades correspondientes a los objetos b1 y b2.


Los operadores sobrecargados son métodos con nombres especiales, donde la palabra
clave operator es seguida por el símbolo del operador a ser definido.
Al igual que cualquier otro método, un operador sobrecargado tiene un tipo de retorno y
una lista de parámetros.
Por ejemplo, para nuestra clase Box, hemos sobrecargado el operador +:
public static Box operator+ (Box a, Box b) {
int h = a.Height + b.Height;
int w = a.Width + b.Width;
Box res = new Box (h, w);
return res;
}

El método anterior define un operador + sobrecargado con dos objetos Box como
parámetros y como retorno un nuevo objeto Box cuyas propiedades Height (alto) y Width
(ancho)son iguales a la suma de las propiedades de los correspondientes parámetros.
Adicionalmente, el operador sobrecargado debe ser estático.
Poniéndolo todo junto:

Herencia
class Box { y polimorfismo
public int Height {get; set;}
 Herencia public int Width {get; set;}
public Box (int h, int w) {
Height = h;
Width = w;
}
public static Box operator+ (Box a, Box b) {
int h = a.Height + b.Height;
int w = a.Width + b.Width;
Box res = new Box (h, w);
return res;
}

}
static void Main (string [] args) {
Box b1 = new Box (14, 3);
Box b2 = new Box (5, 7);
Box b3 = b1 + b2;

Console.WriteLine(b3. Height); //19


Console.WriteLine(b3. Width); //10
}

Todos los operadores aritméticos y de comparación pueden ser sobrecargados. Por


ejemplo, puedes definir operadores mayor que y menor que los cuales compararían
dos objetos Box y retornarían un resultado booleano. Sólo mantén presente que
cuando sobrecargamos el operador mayor que, el operador menor que también
debería ser definido.

La herencia nos permite definir una clase basados en otra clase. Esto facilita la creación y
mantenimiento de una aplicación.
La clase cuyas propiedades son heredadas por otra clase es llamada la clase Base. La clase
que hereda las propiedades es llamada la clase Derivada (Derived).
Por ejemplo, la clase base Animal puede ser utilizada para derivar las clases Cat y Dog.
Las clases derivadas heredan todas las características de la clase base, y puede tener sus
propias características adicionales.
Vamos a definir nuestra clase base Animal:

La herencia nos permite definir una clase basados en otra clase.

class Animal {
public int Legs {get; set;}
public int Age {get; set;}
}
class Dog : Animal {
public Dog () {
Legs = 4;
}
public void Bark () {
Console.Write("Woof");
}
}

Ahora podemos derivar la clase Dog de esta:


Fíjate en la sintaxis para una clase derivada. Un signo de dos puntos y el nombre de la
clase base siguen al nombre de la clase derivada.
Todos los miembros públicos de Animal se vuelven miembros públicos de Dog. Por esto
es que podemos acceder el miembro Legs en el constructor Dog.
Ahora podemos instanciar un objeto del tipo Dog y acceder a los miembros heredados, así
como invocar su propio método Bark.
static void Main (string [] args) {
Dog d = new Dog ();
Console.WriteLine(d.Legs);
// Outputs 4

d.Bark ();
//Outputs "Woof"
}

Una clase base puede tener muchas clases derivadas. Por ejemplo, una clase Cat puede

La herencia permite a la clase derivada reutilizar el código en la clase base sin tener
que reescribirlo. Y la clase derivada puede ser personalizada añadiendo más miembros.
De esta manera, la clase derivada extiende la funcionalidad de la clase base.

heredar de Animal.
Una clase derivada hereda todos los miembros de la clase base, incluyendo sus métodos.
Por ejemplo:

class Person {
public void Speak () {
Console.WriteLine("Hi there");
}
}
class Student : Person {
int number;
}
static void Main (string [] args) {
Student s = new Student ();
s.Speak();
//Outputs "Hi there"
}

Hemos creado un objeto Student e invocamos el método Speak, el cual fue declarado en

C# no soporta herencia múltiple, por lo que no puedes heredar de varias clases a la vez.
Sin embargo, puedes utilizar interfaces para implementar herencia múltiple.

la clase base Person.


 Miembros protegidos
- protected
Hasta este punto, hemos trabajado exclusivamente con los modificadores de acceso
public y private.
Los miembros públicos pueden ser accedidos desde cualquier parte externa a la clase,
mientras que el acceso a los miembros privado está limitado a su clase.
El modificador de acceso protected (protegido) es muy similar a private (privado) con
una diferencia; puede ser accedido en las clases derivadas. Así, un miembro protegido
es accesible sólo desde las clases derivadas.
Por ejemplo:
class Person {
protected int Age {get; set;}
protected string Name {get; set;}
}
class Student : Person {
public Student(string nm) {
Name = nm;
}
public void Speak () {
Console.Write("Name: "+Name);
}
}
static void Main (string [] args) {
Student s = new Student("David");
s.Speak();
//Outputs "Name: David"
}

Como puedes ver, podemos acceder y modificar la propiedad Name de la clase base
desde la clase derivada.
Pero, si intentamos accederla desde código externo, tendremos un error:
- sealed
static void Main (string [] args) {
Student s = new Student("David");
s.Name = "Bob"; //Error
}
sealed class Animal {
//some code
}
class Dog : Animal { } //Error

Una clase puede impedir que otras clases la hereden, o cualquiera de sus miembros,
utilizando el modificador sealed (sellado).
Por ejemplo:
En este caso, no podemos derivar la clase Dog de la clase Animal porque Animal está
sellada (sealed).
 Constructor y destructor de la clase derivada
- Herencia
Los constructores son invocados cuando objetos de una clase son creados. Con la
herencia, el constructor y destructor de la clase base no son heredados, por lo que
debes definir constructores para las clases derivadas.
Sin embargo, el constructor y destructor de la clase base están siendo invocados

La palabra clave sealed provee un nivel de protección para tu clase con el fin de que
otras clases no puedan heredar de ella.

automáticamente cuando un objeto de la clase derivada es creado o eliminado.


Considera el siguiente ejemplo:

class Animal {
public Animal () {
Console.WriteLine("Animal created");
}
~Animal () {
Console.WriteLine("Animal deleted");
}
}
class Dog: Animal {
public Dog () {
Console.WriteLine("Dog created");
}
~Dog () {
Console.WriteLine("Dog deleted");
}
}

Hemos definido la clase Animal con un constructor y un destructor, y una clase


derivada Dog con sus propios constructor y destructor.

Por lo tanto, ¿qué pasa cuando nosotros creamos un objeto de la clase derivada?

Vamos a crear un objeto Dog:

static void Main (string [] args) {


Dog d = new Dog ();
}
/*Outputs
Animal created
Dog created
Dog deleted
Animal deleted
*/

Fíjate que el constructor de la clase base es llamado primero y el constructor de la


clase derivada es llamado a continuación.
Cuando el objeto es destruido, el destructor de la clase derivada es invocado y luego
el destructor de la clase base es invocado.

Puedes imaginarlo de la siguiente forma: La clase derivada necesita su clase base para
que pueda funcionar, lo cual es la razón para que el constructor de la clase base sea
llamado primero.

Polimorfismo
La palabra polimorfismo significa "teniendo muchas formas".
Normalmente, el polimorfismo ocurre cuando hay una jerarquía de clases y éstas están
relacionadas entre sí a través de la herencia desde una clase común.
El polimorfismo significa que una invocación a un método miembro causará una
implementación diferente a ser ejecutada dependiendo del tipo de objeto que invoque el
método.

Simplemente, polimorfismo significa que un sólo método puede tener un número de


implementaciones diferentes.

Considera tener un programa que permita a los usuarios dibujar figuras diferentes. Cada
figura es dibujada diferente, y tú no sabes cuál figura va a seleccionar el usuario.
Aquí, polimorfismo puede apoyar para poder invocar el método Draw apropiado de
cualquier clase derivada al sobrescribir el mismo método de la clase base. Tales métodos
deben ser declarados utilizando la palabra clave virtual en la clase base.
Por ejemplo:
La palabra clave virtual permite que los métodos sean sobrescritos en las clases derivadas.

class Shape {
public virtual void Draw () {
Console.Write("Base Draw");
}
}

class Circle : Shape {


Los métodos
publicvirtuales
overridetevoid
permiten
Draw ()trabajar
{ con grupos de objetos relacionados en una
forma uniforme.// draw a circle...
Console.WriteLine("Circle Draw");
}
}
class Rectangle : Shape {
public override void Draw () {
// draw a rectangle...
Console.WriteLine("Rect Draw");
}
}

Ahora, podemos derivar diferentes clases de formas que definen sus propios métodos
Draw utilizando la palabra clave override:
El método virtual Draw en la clase base Shape puede ser sobrescrito en la clase derivada.
En este caso, Circle y Rectangle tienen sus propios métodos Draw.
Ahora, podemos crear objetos Shape por separado para cada tipo derivado y luego llamar
sus métodos Draw:

static void Main (string [] args) {


Shape c = new Circle ();
c.Draw ();
//Outputs "Circle Draw"

Shape r = new Rectangle ();


r.Draw();
//Outputs "Rect Draw"
}
Para resumir, polimorfismo es una forma de invocar el mismo método para diferentes
objetos y generar diferentes resultados basados en el tipo de objeto. Este
comportamiento es logrado a través de métodos virtuales en la clase base.
Para implementar esto, creamos objetos del tipo base, pero los instanciamos como el tipo
derivado:
Shape es la clase base. Circle es la clase derivada.

Shape c = new Circle ();

Circle c = new Circle ();


c.Draw ();

Entonces, ¿por qué utilizar polimorfismo? Pudiéramos simplemente instanciar cada


objeto con su mismo tipo y llamar a sus métodos, de la siguiente forma:
El enfoque de polimorfismo nos permite tratar a cada objeto de la misma forma. Como
todos los objetos son del tipo Shape, es más fácil mantenerlos y trabajar con ellos.
Podrías, por ejemplo, tener una lista (o arreglo) de objetos de ese tipo y trabajar con ellos
dinámicamente, sin tener que conocer el tipo actual derivado de cada objeto.
 Clases abstractas
Como fue descrito en el ejemplo anterior, polimorfismo es utilizado cuando tienes
diferentes clases derivadas con el mismo método, el cual tiene diferentes
implementaciones en cada clase. El comportamiento es logrado a través de métodos

El polimorfismo puede ser útil en muchos casos. Por ejemplo, podemos crear un juego
donde tendríamos diferentes tipos de Player (Jugador) donde cada Player (Jugador)
tendría un comportamiento diferente para el método Attack (Ataque).
En este caso, Attack sería un método virtual de la clase base Player y cada clase
derivada lo sobrescribiría.

virtuales que son sobrescritos en las clases derivadas.


En algunas situaciones no hay una necesidad significativa para que el método virtual
tenga una definición separada en la clase base.
Estos métodos son definidos utilizando la palabra clave abstract (abstracto) y especifica
que las clases derivadas deben definir ese método a su manera.
No puedes crear objetos de una clase que contenga un método abstracto, lo cual es la
razón para que la propia clase deba ser abstracta.
Podríamos utilizar un método abstracto en la clase Shape:
Como puedes ver, el método Draw es abstracto y por lo tanto no tiene cuerpo. Ni siquiera

abstract class Shape {


public abstract void Draw ();
}

necesitas las llaves; sólo finalizar la declaración con un punto y coma.


La propia clase Shape debe ser declarada abstracta porque contiene un método
abstracto. Las declaraciones de métodos abstractos sólo son permitidas en clases
abstractas.
Recuerda, las declaraciones de métodos abstractos sólo son permitidas en clases
abstractas. Los miembros marcados como abstractos, o incluido en una clase abstracta,
deben ser implementados por clases que derivan de la clase abstracta. Una clase
abstracta puede tener múltiples miembros abstractos.

La intención de una clase abstracta es que sea una clase base de otra clase. Actúa como
una plantilla para sus clases derivadas.
Ahora, teniendo la clase abstracta, podemos derivar las otras clases y definir sus propios
métodos Draw ():

abstract class Shape {


public abstract void Draw ();
}
class Circle : Shape {
public override void Draw () {
Console.WriteLine("Circle Draw");
}
}
class Rectangle : Shape {
public override void Draw () {
Console.WriteLine("Rect Draw");
}
}
static void Main (string [] args) {
Shape c = new Circle ();
c.Draw ();
//Outputs "Circle Draw"
}

Las clases abstractas tienen las siguientes características:


- Una clase abstracta no puede ser instanciada.
- Una clase abstracta puede contener métodos abstractos y descriptores de acceso.
- Una clase no-abstracta derivada de una clase abstracta debe incluir la verdadera
implementación de todos los métodos abstractos heredados y sus descriptores de acceso.
 Interfaces
No es posible modificar una clase abstracta con el modificador sealed porque los dos
modificadores tienen significados opuestos. El modificador sealed previene que una
clase tenga herencia y el modificador abstract requiere que una clase sea heredad por
otra.

Una interfaz es una clase completamente abstracta, la cual contiene solamente miembros
abstractos.
Estas son declaradas utilizando la palabra clave interface:

public interface IShape {


void Draw ();
}
Todos los miembros de la interfaz son por defecto abstractos, por lo que no es necesario
utilizar la palabra clave abstract.
También, todos los miembros de una interfaz son siempre públicos, y ningún modificador
de acceso puede ser aplicado a ellos.

Es común utilizar la letra mayúscula I como la letra inicial para el nombre de una
interfaz.
Las interfaces pueden contener propiedades, métodos, etc. pero no pueden contener
campos (variables).

Cuando una clase implementa una interfaz, también debe implementar, o definir, todos
sus métodos.
El término implementando una interfaz es utilizado (opuesto al término "heredando de")
para describir el proceso de crear una clase basada en una interfaz. La interfaz describe
simplemente lo que una clase debe hacer. La clase que está implementando la interfaz
debe definir cómo lograr los comportamientos.
La sintaxis para implementar una interfaz es la misma que la de derivar una clase:

public interface IShape {


void Draw ();
}
class Circle : IShape {
public void Draw () {
Console.WriteLine("Circle Draw");
}
}
static void Main (string [] args) {
IShape c = new Circle ();
c.Draw ();
//Outputs "Circle Draw"
}

Fíjate que la palabra clave override no es requerida cuando estás implementando una

Pero, ¿por qué utilizar interfaces en lugar de clases abstractas?


Una clase puede heredar de sólo una clase base, pero ¡puede implementar múltiples
interfaces!
Por lo tanto, al utilizar interfaces puedes incluir comportamientos de múltiples fuentes
en una clase.
Para implementar varias interfaces, utiliza una lista separada por comas de interfaces
cuando crees la clase: class A: IShape, IAnimal, etc.

interfaz.
 Clases anidadas
class Car {
C# soporta clases
string anidadas: una clase que es un miembro de otra clase.
name;
Por ejemplo:
public Car (string nm) {
name = nm;
Motor m = new Motor ();
}
public class Motor {
// some code
}
}
La clase Motor está anidada en la clase Car y puede ser utilizada de forma similar que
otros miembros de la clase.
Una clase anidada actúa como un miembro de la clase, por lo que puede tener los mismos
modificadores de acceso como otros miembros (public, private, protected).
 Namespaces
Al igual que en la vida real, los objetos pueden contener otros objetos. Por ejemplo, un
carro, el cual tiene sus propios atributos (color, marca, etc.) contiene un motor, el cual,
como System;
using un objeto separado, tiene sus propios atributos (volumen, caballos de fuerza,
etc.). Aquí, la clase Car puede tener una clase Motor anidada como uno de sus
using System.Collections.Generic;
miembros.
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SoloLearn {
class Program {
static void Main (string [] args) {
}
}
}

Cuando creas un proyecto en blanco, tiene la siguiente estructura:


Fíjate, que nuestro programa completo está dentro de un namespace. Entonces, ¿qué son
namespaces?
Los namespaces declaran el alcance que contiene un conjunto de objetos relacionados.
Puedes utilizar un namespace para organizar elementos del código. Puedes definir tus
propios namespaces y utilizarlos en tu programa.
La palabra clave using declara que el programa está utilizando un namespace dado.
Por ejemplo, estamos utilizando el namespace System en nuestros programas, que es
donde la clase Console está definida:

using System;
...
Console.WriteLine("Hi");

System.Console.WriteLine("Hi");

El Framework .NET utiliza namespaces para organizar sus múltiples clases. System es
un ejemplo de un namespace del Framework .NET.
Declarar tus propios namespaces puede ayudarte a agrupar los nombres de tus clases y
métodos en proyectos de programación más grandes.

Sin la declaración using, tendríamos que especificar el namespace donde es utilizado:

Estructuras, Enumeraciones, Excepciones y Archivos


 Estructuras
Una estructura (struct) es un tipo de valor que es utilizado usualmente para encapsular
struct Book {
un grupo pequeño de variables relacionadas, como las coordenadas de un rectángulo o
public string title;
public double price;
public string author;
}
las características de un artículo en un inventario. El siguiente ejemplo muestra la
declaración de una estructura simple:
Las estructuras comparten mucho de la misma sintaxis de las clases, pero son más
limitadas que las clases.
A diferencia de las clases, las estructuras pueden ser instanciadas sin utilizar un operador
new.

static void Main (string [] args) {


Book b;
b.title = "Test";
b.price = 5.99;
b.author = "David";

Console.WriteLine(b.title);
//Outputs "Test"
}

Las estructuras no soportan herencia y no pueden contener métodos virtuales.

Las estructuras pueden contener métodos, propiedades, indexadores y así sucesivamente.


Las estructuras no pueden contener constructores predeterminados (un constructor sin
parámetros), pero pueden tener constructores que toman parámetros. En ese caso, la
palabra clave new es utilizada para instanciar un objeto estructura, similar a los objetos de
clases.
Por ejemplo:

struct Point {
public int x;
public int y;
public Point (int x, int y) {
this.x = x;
this.y = y;
}
}
static void Main (string [] args) {
Point p = new Point (10, 15);
Console.WriteLine(p.x);
// Outputs 10
}

- Estructuras vs Clases
En general, las clases son utilizadas para modelar comportamiento o datos más
complejos, que se tiene la intención de modificar después que el objeto de clase es
creado. Las estructuras están mejor adaptadas para pequeñas estructuras de datos
que contienen principalmente datos que no se tiene la intención de modificar
después que la estructura es creada. Considera definir una estructura en lugar de una
clase si estás tratando de representar un conjunto de datos simple.

Todos los tipos estándar de C# (int, double, bool, char, etc.) son realmente estructuras.
 Enumeraciones
La palabra clave enum es utilizada para declarar una enumeración: un tipo que consiste
en un conjunto de nombres constantes llamados la lista del enumerador.
Por defecto, el primer enumerador tiene el valor 0, y el valor de cada enumerador
sucesivo es incrementado por 1.
Por ejemplo, en la siguiente enumeración, Sun es 0, Mon es 1, Tue es 2, y así
sucesivamente:
También puedes asignar tus propios valores de enumerador:

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

enum Days {Sun, Mon, Tue = 4, Wed, Thu, Fri, Sat};

En el ejemplo anterior, la enumeración comenzará desde 0, luego Mon es 1, Tue es 4,


Wed es 5 y así sucesivamente. El valor del siguiente elemento en un Enum es un
incremento del valor previo.
Fíjate que los valores están separados por comas.
Puedes referirte a los valores en el Enum con la sintaxis de punto
Para poder asignar valores de Enum a variables int, tienes que especificar el tipo entre
paréntesis:

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

static void Main (string [] args) {


int x = (int)Days.Tue;
Console.WriteLine(x);
//Outputs 2
}

Básicamente, los Enums definen variables que representan miembros de un conjunto


prefijado.
Algunos ejemplos de uso de Enum incluyen nombres de meses, días de la semana,
cartas en una baraja, etc.

Las enumeraciones son utilizadas usualmente con declaraciones switch.


enum
Por TrafficLights {Green, Red, Yellow};
ejemplo:

static void Main (string [] args) {


TrafficLights x = TrafficLights.Red;
 Excepciones
switch (x) {
case TrafficLights.Green:
Console.WriteLine("Go!");
break;
case TrafficLights.Red:
Console.WriteLine("Stop!");
break;
case TrafficLights.Yellow:
Console.WriteLine("Caution!");
break;
}
//Outputs "Stop!"
}
Una excepción es un problema que ocurre durante la ejecución del programa. Las
excepciones causan terminación anormal del programa.
Una excepción puede ocurrir por muchas razones diferentes. Algunos ejemplos:
- Un usuario ha ingresado datos inválidos.
- Un archivo que necesita ser abierto no pudo ser encontrado.
- Se perdió una conexión de red en el medio de unas comunicaciones.
- Insuficiente memoria y otros problemas asociados a recursos físicos.

Por ejemplo, el siguiente código producirá una excepción cuando sea ejecutado porque
solicitamos un índice el cual no existe:

int [] arr = new int [] {4, 5, 8};


Console.Write(arr [8]);

Como puedes ver, las excepciones son causadas por errores del usuario, errores del
programador o problemas de recursos físicos. Sin embargo, un programa bien escrito
debería manejar todas las excepciones posibles.

- Manejando excepciones
C# provee un mecanismo flexible llamado la declaración try-catch para manejar
excepciones evitando que un programa se detenga cuando un error ocurre.
Los bloques try y catch son utilizados de manera similar a:

try {
int [] arr = new int [] {4, 5, 8};
Console.Write(arr [8]);
}
catch (Exception e) {
Console.WriteLine("An error occurred");
}
//Outputs "An error occurred"

El código que puede generar una excepción es colocado en el bloque try. Si una
excepción ocurre, el bloque catch es ejecutado sin detenerse el programa.
El tipo de excepción que quieres capturar aparece entre paréntesis después de la
palabra clave catch.
Utilizamos el tipo genérico Exception para manejar todo tipo de excepciones.
También podemos utilizar el objeto Exception e para acceder a los detalles de la
excepción, como el mensaje de error original (e.Message):
- Manejando múltiples excepciones
try {
int [] arr = new int [] {4, 5, 8};
Console.Write(arr [8]);
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
//Index was outside the bounds of the array.

También puedes capturar y manejar diferentes excepciones por separado.


Un sólo bloque try puede contener múltiples bloques catch que manejan diferentes
excepciones por separado.
El manejo de excepciones es particularmente útil cuando estamos manejando
entradas del usuario.
Por ejemplo, para un programa que requiere entrada de usuario de dos números y
que luego despliegue su cociente, asegúrate que manejes la división entre cero, en
caso de que el usuario ingrese 0 como el segundo número.

int x, y;
try {
x = Convert.ToInt32(Console.Read());
y = Convert.ToInt32(Console.Read());
Console.WriteLine(x / y);
}
catch (DivideByZeroException e) {
Console.WriteLine("Cannot divide by 0");
}
catch (Exception e) {
Console.WriteLine("An error occurred");
}

El código anterior maneja la excepción DivideByZeroException por separado. El


último catch maneja todas las otras excepciones que puedan ocurrir. Si múltiples
excepciones son manejadas, el tipo Exception debe ser el último en ser definido.
Ahora, si el usuario ingresa 0 como el segundo número, "Cannot divide by 0" será
desplegado.
Si, por ejemplo, el usuario ingresa valores no-enteros, "An error occurred" será
desplegado.
- Finally
Los siguientes tipos de excepciones son algunos de los más comúnmente utilizados:
FileNotFoundException, FormatException, IndexOutOfRangeException,
InvalidOperationException, OutOfMemoryException.

Un bloque opcional finally puede ser utilizado después de los bloques catch. El bloque
finally es utilizado para ejecutar un conjunto de declaraciones dado, se haya lanzado
una excepción o no.
Por ejemplo:

int result=0;
int num1 = 8;
int num2 = 4;
try {
result = num1 / num2;
}
catch (DivideByZeroException e) {
Console.WriteLine("Error");
}
finally {
Console.WriteLine(result);
}
El bloque finally puede ser utilizado, por ejemplo, cuando trabajas con archivos u otros
recursos. Estos deben ser cerrados o liberados en el bloque finally, bien sea que se
levante una excepción o no.
 Trabajando con archivos
- Escribiendo en archivos
El namespace System.IO tiene varias clases que son utilizadas para realizar numerosas
operaciones con archivos, tales como crear y borrar archivos, leer desde o escribir a
un archivo, cerrar un archivo, y más.
La clase File es una de ellas. Por ejemplo:
El método WriteAllText () crea un archivo con la dirección especificada y escribe el

string str = "Some text";


File.WriteAllText("test.txt", str);

contenido en él. Si el archivo ya existe, será sobrescrito.


El código anterior crea un archivo test.txt y escribe el contenido del string str en él.
- Leyendo de archivos

Para utilizar la clase File necesita utilizar el namespace System.IO: using System.IO;

string txt = File.ReadAllText("test.txt");


Console.WriteLine(txt);

Puedes leer el contenido de un archivo utilizando el método ReadAllText de la clase


File:
Esto dará como salida el contenido del archivo test.txt.
Los siguientes métodos están disponibles en la clase File:
AppendAllText () - añade texto al final del archivo.
Create () - crea un archivo en la ubicación especificada.
Delete () - elimina el archivo especificado.
Exists () - determina si el archivo especificado existe.
Copy () - copia un archivo a una nueva ubicación.
Move () - mueve un archivo específico a una nueva ubicación

Genéricos
Todos los métodos cierran automáticamente el archivo después de realizar la
 operación.

Métodos genéricos
Los genéricos permiten reutilizar el código a través de diversos tipos.
Por ejemplo, vamos a declarar un método que intercambia los valores de sus dos
parámetros:

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


int temp = a;
a = b;
b = temp;
}

Nuestro método Swap funcionará sólo para parámetros enteros. Si queremos utilizarlo
para otros tipos, por ejemplo, dobles o strings, tenemos que sobrecargar el método para
todos los tipos con los que queramos utilizarlo. Adicional a una gran cantidad de código
repetido, se vuelve más difícil de administrar el código porque cambios en un método
significa cambios en todos los métodos sobrecargados.
Los genéricos proveen un mecanismo flexible para definir un tipo genérico.

static void Swap<T> (ref T a, ref T b) {


T temp = a;
a = b;
b = temp;
}

En el código anterior T es el nombre de nuestro tipo genérico. Podemos ponerle el


nombre que queramos, pero T es un nombre comúnmente utilizado. Nuestro método
Swap ahora toma dos parámetros del tipo T. También utilizamos el tipo T para nuestra
variable temp que es utilizada para intercambiar los valores.

Fíjate en la sintaxis con los corchetes angulares en <T>, los cuales son utilizados para
definir un tipo genérico.

Ahora, podemos utilizar nuestro método Swap con diferentes tipos, como se muestra a
continuación:

static void Swap<T> (ref T a, ref T b) {


T temp = a;
a = b;
b = temp;
}
static void Main (string [] args) {
int a = 4, b = 9;
Swap<int> (ref a, ref b);
//Now b is 4, a is 9

string x = "Hello";
string y = "World";
Swap<string> (ref x, ref y);
//Now x is "World", y is "Hello"
}

Cuando se invoca un método genérico, necesitamos especificar el tipo con el que


trabajará utilizando corchetes angulares (<>). Así, cuando Swap<int> es invocado, el tipo T
es reemplazado por int. Para Swap<string>, T es reemplazado por string.
Si omites especificar el tipo al invocar un método genérico, el compilador utilizará el tipo
basado en los argumentos pasados al método.
 Clases genéricas
Múltiples parámetros genéricos pueden ser utilizados con un sólo método.
Por ejemplo: Func<T, U> toma dos diferentes tipos genéricos.

Los tipos genéricos pueden también ser utilizados con clases.


El uso más común para las clases genéricas es con colecciones de elementos, donde las
operaciones como añadir y remover elementos de la colección son realizadas
básicamente de la misma manera independientemente del tipo de dato que está siendo
almacenado. Un tipo de colección es llamado un stack (pila). Los elementos son
"empujados", o añadidos a la colección y "sacados", o removidos de la colección. Un stack
es a veces llamado una estructura de datos Último en Entrar, Primero en Salir (LIFO por
sus siglas en inglés).
Por ejemplo:

class Stack<T> {
int index=0;
T [] innerArray = new T [100];
public void Push (T item) {
innerArray[index++] = item;
}
public T Pop () {
return innerArray [--index];
}
public T Get (int k) {
return innerArray[k];
}
}

La clase genérica almacena elementos en un arreglo. Como puedes ver, el tipo genérico T
es utilizado como el tipo del arreglo, el tipo del parámetro para el método Push, y el tipo
del retorno para los métodos Pop y Get.
Ahora podemos crear objetos de nuestra clase genérica:

Stack<int> intStack = new Stack<int> ();


Stack<string> strStack = new Stack<string> ();
Stack<Person> PersonStack = new Stack<Person> ();

También podemos utilizar clases genéricas con tipos personalizados, como el tipo definido

En una clase genérica no necesitamos definir el tipo genérico para sus métodos, porque
el tipo genérico ya está definido a nivel de la clase.

personalizado Person.
Los métodos genéricos de clase son invocados de la misma forma como cualquier otro
objeto:
 Collections
Una collection se usa para agrupar objetos relacionados. A diferencia de un array, que
también puede agrupar objetos, una collection es dinámica. Puede crecer y decrecer para
acomodar cualquier cantidad de objetos. Dado que las clases de collections están

Stack<int> intStack = new Stack<int> ();


intStack.Push(3);
intStack.Push(6);
intStack.Push(7);

Console.WriteLine(intStack.Get(1));
//Outputs 6
organizadas en un namespace o espacio de nombres, tienen muchos métodos para
procesar la collection integrados.
Una colección organiza los datos relacionados en una computadora para que pueda ser
utilizada de manera eficiente.
Diferentes tipos de colecciones se adaptan a diferentes tipos de aplicaciones, y algunas
están altamente especializadas para tareas específicas. Por ejemplo, los diccionarios se
utilizan para representar conexiones en sitios web sociales (como Twitter, Facebook), las
colas se pueden usar para crear programadores de tareas, HashSets se utilizan en los
algoritmos de búsqueda, etc.
Una collection suele incluir métodos para agregar, quitar y contar objetos. La declaración
for y la declaración foreach se utilizan para iterar en una collection. Igual que en todas las
clases, primero tienes que declarar una instancia de una collection antes de añadir
elementos a ésta.
- Collections Generales
List<int> li = new List<int> ();

Las colecciones proporcionan una forma más flexible de trabajar con grupos de
objetos. A diferencia de las matrices, el grupo de objetos con los que trabaja puede
crecer y reducirse dinámicamente a medida que cambian las necesidades de la
aplicación.

El mejor tipo de collection que puedes usar es una collection genérica porque todos
los elementos están fuertemente tipados, lo que reduce la posibilidad de errores.
El framework .NET provee un número de clases genéricas de colecciones, útiles para
almacenar y manipular datos.
El espacio de nombres System.Collections.Generic incluye lo siguiente:
- List<T>
- Dictionary<TKey, TValue>
- SortedList<TKey, TValue>
- Stack<T>
- Queue<T>
- Hashset<T>
Para acceder a una collection genérica en tu código, tendrás que incluir la siguiente
declaración: using Systems.Collections.Generic;
- Collections no generales
Las collections no generales pueden almacenar elementos de tipo Object (objeto).
Dado que un tipo de datos Object puede hacer referencia a cualquier tipo de datos,
corres el riesgo de obtener resultados inesperados. Las collections no genéricas
también son más lentas de acceder y de ejecutar. El tipado fuerte, como ocurre con
las collections genéricas, es un mejor estilo de programación.
El espacio de nombres System.Collections incluye las siguientes collections no
genéricas:
- ArrayList
- SortedList
- Stack
- Queue
- Hashtable
- BitArray

Siempre que sea posible, utiliza las collections genéricas en el espacio de nombres
System.Collections.Generic en lugar de los tipos tradicionales del espacio
System.Collections.
 List y BitArray
- List
Una list (lista) es como un array, con la diferencia considerable de que los elementos
pueden ser insertados o eliminados de forma dinámica.
La clase de collection genérica de C# List<T> requiere que todos los elementos sean
del mismo tipo T.
Las propiedades y métodos de List<T> incluyen:
Count Una propiedad que devuelve la cantidad de elementos de la lista.
Item [int i] Devuelve o establece el valor en una lista con el índice (i). Item es otro
nombre para el indizador y no es necesario cuando se accede al elemento. Sólo tienes
que usar los corchetes [] y el valor del índice.
Add (T t) Añade un elemento (t) al final de la lista.
RemoveAt (int index) Elimina el elemento en la posición especificada (index) de la
lista.
Sort () Ordena la lista.
Probemos ahora List<T>:

List<int> li = new List<int> ();


li.Add (59);
...
li.RemoveAt (1);

Console.Write("\nList: ");
for (int x = 0; x < li.Count; x++)
Console.Write(li[x] + " ");
li.Sort ();
Console.Write("\nSorted: ");
for (int x = 0; x < li.Count; x++)
Console.Write(li[x] + " ");

A continuación, aparecen propiedades y métodos adicionales de List<T>. Pruébalos


añadiéndolos al código de ejemplo de List<T> de arriba.
Capacity Una propiedad que devuelve la cantidad de elementos que puede contener
la lista antes de necesitar un cambio de tamaño.
Clear () Elimina todos los elementos de la lista.
TrimExcess () Establece la capacidad según la cantidad de elementos de la lista (es útil
cuando se quiere reducir la sobrecarga de memoria).
AddRange (IEnumerable coll) Añade los elementos de una collection (coll) con
elementos del mismo tipo como List<T> al final de la lista. IEnumerable es la interfaz
de collections que admite una iteración simple sobre el rango.
Insert (int index, T t) Inserta un elemento (t) en una posición específica (index) de la
lista.
InsertRange(int index, IEnumerable coll) Inserta los elementos de una collection (coll)
en una posición específica (index) de la lista. IEnumerable es la interfaz de collections
que admite una iteración simple sobre el rango.
Remove (T t) Elimina un elemento (t) de la lista.
RemoveRange (int index, int count) Elimina una cantidad especificada de elementos
(count) de la lista a partir de una posición especificada (index).
Contains (T t) Devuelve true (verdadero) cuando está presente un elemento (t) en la
lista.
IndexOf (T t) Devuelve el índice de la primera aparición de un elemento en la lista.
Reverse () Invierte el orden de los elementos en una lista.
ToArray () Crea un array a partir de la lista.
- SortedList<K, V>

Recuerda que para usar List<T> tienes que incluir la siguiente declaración: using
Systems.Collections.Generic;

Una sorted list (lista ordenada) es una collection de pares key-value (clave-valor) que
se ordenan por el valor de la clave. Se puede usar una key o clave para acceder a su
value (valir) correspondiente en la lista ordenada.
La clase de collection genérica de C# SortedList<K, V> requiere que todos los
elementos keys/values sean del mismo tipo K/V. No se permiten keys duplicadas, lo
que asegura que todos los pares key-value son únicos.
Las propiedades de SortedList<K, V> incluyen:
Count Devuelve la cantidad de pares key-value de la lista ordenada.
Item [K key] Devuelve el valor asociado con una clave especificada (key) de la lista
ordenada. Item es otro nombre para el indizador y no es necesario cuando se accede
al elemento. Sólo tienes que usar los corchetes [] y el valor de la clave.
Keys Devuelve una collection ordenada e indexada que sólo contiene las claves de la
lista ordenada.
Los métodos incluyen:
Add (K key, V value) Añade un par key-value (key, value) a la lista ordenada.
Remove (K key) Elimina el par key-value asociado con la clave especificada (key) de la
lista ordenada.
Probemos ahora SortedList<K, V>:

SortedList<string, int> sl = new SortedList<string, int> ();


sl.Add("Solo", 59);
sl.Add("A", 95);
sl.Add("Learn", 72);
sl.Remove("A");
Console.WriteLine("Sorted List: ");
foreach (string s in sl.Keys)
Console.WriteLine(s + ": " + sl[s]); // Learn: 72 Solo: 59
Console.WriteLine("\nCount: " + sl.Count); // 2

Aquí tienes propiedades y métodos adicionales SortedList<K, V>:


Values Devuelve una collection ordenada e indexada de los valores de la lista
ordenada.
Clear () Elimina todos los elementos de la lista ordenada.
ContainsKey (K key) Devuelve true (verdadero) cuando está presente una clave (key)
en la lista ordenada.
ContainsValue (V value) Devuelve true (verdadero) cuando está presente un valor
(value) en la lista ordenada.
IndexOfKey (K key) Devuelve el índice de una clave especificada de la lista ordenada.
IndexOfValue (V value) Devuelve el índice de un valor especificado de la lista
ordenada.
- BitArray
Un bit array o matriz de bits es una collection (colección) de bits. El valor de un bit
puede ser 0 (falso/off) o 1 (verdadero/on).
Un bit array almacenan de forma compacta los bits. Más comúnmente, se usan para
representar un grupo simple de banderas booleanas o una secuencia ordenada de
valores booleanos.
Las propiedades de BitArray incluyen:
Count: La cantidad de bits en el bitarray.
IsReadOnly: Un valor que indica si el bitarray es de sólo lectura o no.
Los métodos de BitArray incluyen:
Get (int index) Devuelve el valor del bit en un índice (index) especificado en el
bitarray.
Set (int index, bool value) establece el bit de un índice específico a un valor
especificado en el bitarray.
SetAll (bool value) establece todos los bits de un valor especificado (value) en el
bitarray.
And (BitArray ba) realiza la operación bit a bit AND en el bitarray (ba).
Or (BitArray ba) realiza la operación bit a bit OR con un bitarray especificado.
Not () realiza la operación bitwise NOT.
Xor (BitArray ba) realiza la operación bit a bit XOR con un bitarray especificado.
Exte ejemplo demuestra algunas propiedades y métodos de la clase BitArray.

static void Main (string [] args) {


BitArray ba1 = new BitArray (4);
BitArray ba2 = new BitArray (4);

ba1.SetAll(true);
ba2.SetAll(false);

ba1.Set(2, false);
ba2.Set(3, true);

PrintBarr ("ba1", ba1);


PrintBarr ("ba2", ba2);
Console.WriteLine();

PrintBarr ("ba1 AND ba2", ba1.And(ba2));


PrintBarr (" NOT ba2", ba2.Not());
}

static void PrintBarr (string name, BitArray ba) {


Console.Write(name + " : ");
for (int x = 0; x < ba.Length; x++)
Console.Write(ba.Get(x) + " ");
Console.WriteLine();
}

Los BitArrays o matrices de bits pueden utilizarse, por ejemplo, en el procesamiento


de imágenes para almacenar los bits individuales de una imagen en escala de grises.

 Stack y Queue
- Stack<T>
Un stack es una collection de elementos Last In, First Out (LIFO) en la que el último
elemento en entrar en el stack será el primero en salir.
Insertar un elemento en un stack recibe el nombre de pushing ("depositar"). Eliminar
un elemento de un stack recibe el nombre de popping. Pushing y popping sólo
pueden hacerse al final de un stack, y ese final recibe el nombre de top.
La collection genérica de C# Stack<T> requiere que todos los elementos sean del
Las pilas se pueden usar para crear funcionalidades de deshacer y rehacer, analizar
expresiones (infijo a conversión de prefijo / postfijo) y mucho más.

mismo tipo T.
Las propiedades de Stack<T> incluyen:
Count Devuelve la cantidad de elementos del stack.
Los métodos de Stack<T> incluyen:
Peek () Devuelve el elemento del inicio del stack sin eliminarlo.
Pop () Devuelve el elemento del inicio del stack y lo elimina.
Push (T t) Inserta un elemento (t) en el inicio del stack.
Probemos ahora Stack<T>:

Stack<int> s = new Stack<int> ();

s.Push(59);
...
Console.Write("Stack: ");
foreach (int i in s)
Console.Write(i + " "); // 65 72 59
Console.Write("\nCount: " + s.Count); // 3

Console.Write("\nTop: " + s.Peek()); // 65


Console.Write("\nPop: " + s.Pop()); // 65

Console.Write("\nStack: ");
foreach (int i in s)
Console.Write(i + " "); // 72 59
Console.Write("\nCount: " + s.Count); // 2

Aquí tienes algunos métodos adicionales de Stack<T>:


Clear () Elimina todos los elementos del Stack.
Contains (T t) Devuelve true (verdadero) cuando está presente un elemento (t) en el
stack.
ToArray () Crea un array a partir del stack.
- Queue<T>
Una queue o cola es una collection de elementos First In, First Out (FIFO) en la que el
primer elemento que entra en la cola será el primer elemento en salir.
Insertar un elemento en una queue recibe el nombre de Enqueue. Eliminar un
elemento de una queue recibe el nombre de Dequeue.

Las colas se usan cuando necesitamos administrar objetos en orden comenzando con el
primero en.
Los escenarios incluyen la impresión de documentos en una impresora, sistemas de
centros de llamadas que responden a personas en espera, y así sucesivamente.
La collection genérica de C# Queue<T> requiere que todos los elementos sean del
mismo tipo T.
Las propiedades de Queue<T> incluyen:
Count Devuelve la cantidad de elementos en la cola.
Y los métodos incluyen:
Dequeue () Devuelve el elemento al principio de la cola y lo elimina.
Enqueue (T t) Inserta un elemento (t) al final de la cola.s an element (t) at the end of
the queue.
Probemos ahora Queue<T>:

Queue<int> q = new Queue<int> ();

q.Enqueue(5);
q.Enqueue(10);
q.Enqueue(15);
Console.Write("Queue: ");
foreach (int i in q)
Console.Write(i + " "); // 5 10 15
Console.Write("\nCount: " + q.Count); // 3

Console.Write("\nDequeue: " + q.Dequeue()); // 5

Console.Write("\nQueue: ");
foreach (int i in q)
Console.Write(i + " "); // 10 15
Console.Write("\nCount: " + q.Count); // 2

Aquí tienes métodos adicionales de Queue<T>:


Clear () Elimina todos los elementos de la cola.
Contains (T t) Deuelve true cuando un elemento (t) está presente en la cola.
Peek () Devuelve el elemento al principio de la cola sin eliminarlo.
ToArray () Crea un array a partir de la cola.
 Dictionary y HashSet
- Dictionary<U, V>
Un dictionary es una collection de pares key-value (clave-valor) únicos en los que se
usa una key para acceder al valor correspondiente.
La collection genérica de C# Dictionary<K, V> requiere que todos los elementos
keys/values sean del mismo tipo K/V. No se permiten keys duplicadas, lo que asegura
que todos los pares key-value son únicos.
Las propiedades de Dictionary<K, V> incluyen:
Count Devuelve la cantidad de pares key-value del dictionary.
Item [K key] Devuelve el valor asociado con una clave especificada (key) del
dictionary. Item es otro nombre para el indizador y no es necesario cuando se accede
al elemento. Sólo tienes que usar los corchetes [] y el valor de la clave.
Keys Obtiene una collection indexada que sólo contiene las claves del dictionary.
Y los métodos incluyen:
Add (K key, V value) Añade un par clave-valor (key, value) al dictionary.
Remove (K key) Elimina el par key-value con la clave especificada (key) del dictionary.
Probemos ahora Dictionary<K, V>:

Dictionary<string, int> d = new Dictionary<string, int> ();


d.Add ("Uno", 1);
d.Add ("One", 1);
...
d.Remove("One"); // Remove key-value pair One, 1
...
Console.WriteLine("Dictionary: ");
foreach (string s in d.Keys)
Console.WriteLine(s + ": " + d[s]); // Uno: 1 Deux: 2
Console.WriteLine("\nCount: {0}", d.Count); // 2

En el ejemplo anterior, el dictionary d usa strings como sus claves y números enteros
como los valores.
Aquí tienes las propiedades y métodos adicionales de Dictionary<K, V>:
Keys Obtiene una collection indexada que sólo contiene los valores del dictionary.
Clear () Elimina todos los elementos del dictionary.
ContainsKey (K key) Devuelve true cuando una clave (key) está presente en el
dictionary.
ContainsValue (V value) Devuelve true cuando un valor (value) está presente en el
dictionary.
- HashSet<T>
Un hash set es un conjunto (set) de valores que no permite valores duplicados.
C# incluyes la clase HashSet<T> en el espacio de nombre de collections genéricas. Se
requiere que todos los elementos de HashSet<T> sean del mismo tipo T.
Los Hash sets o conjuntos de hashes son diferentes de otras collections porque
simplemente son un conjunto de valores. No tienen posiciones indexadas y los
elementos no se pueden ordenar.

La clase HashSet <T> proporciona operaciones de conjunto de alto rendimiento. Los


HashSets permiten una rápida búsqueda, adición y eliminación de elementos, y se
pueden usar para implementar conjuntos dinámicos de elementos o tablas de
búsqueda que permitan encontrar un elemento por su clave (por ejemplo, encontrar el
número de teléfono de una persona por su apellido).

Las propiedades de HashSet<T> incluyen:


Count Devuelve la cantidad de valores del hash set.
Y los métodos incluyen:
Add (T t) Añade un alor (t) al hash set.
IsSubsetOf (ICollection c) Devuelve true si el hash set es un subconjunto (subset) de la
collection especificada (c).
Probemos ahora HashSet<T>:

HashSet<int> hs = new HashSet<int> ();


hs.Add(5);
...
Console.Write("\nHashSet: ");
foreach (int i in hs)
Console.Write(i + " "); // 5 10 15 20 *elements may be in any order
Console.Write("\nCount: " + hs.Count); // 4

HashSet<int> hs2 = new HashSet<int> ();


hs2.Add(15);
hs2.Add(20);
Console.Write("\n{15, 20} is a subset of {5, 10, 15, 20}: " + hs2.IsSubsetOf(hs)); // True

Aquí tienes métodos adicionales de HashSet<T>:


Remove (T t) Elimina el valor (t) del hash set.
Clear () Elimina todos los elementos del hash set.
Contains (T t) Devuelve true cuando está presente un valor (t) en el hash set.
ToString () Crea un string a partir del hash set.
IsSupersetOf (ICollection c) Devuelve true si el hash set es un superconjunto
(superset) de la collection especificada.
UnionWith (ICollection c) Aplica la operación de conjunto (set operation) union en el
hash set y en la collection especificada (c).
IntersectWith (ICollection c) Aplica la operación de conjunto intersection en el hash
set y la collection especificada (c).
ExceptWith (ICollection c) Aplica la operación de conjunto difference en el hash set y
la collection especificada (c).

Más sobre el tema


 C # Consejos para Juniors
Este artículo destaca algunos aspectos clave en los que los desarrolladores deberían
centrarse al escribir código.
- Escribir código limpio
Use nombres descriptivos para variables, funciones y clases.
En su carrera trabajará con otros desarrolladores, leerá el código de otros
desarrolladores y, seguramente, otros leerán su código.
Por ejemplo, mire el código a continuación y compare las dos versiones de la misma
// ugly version
operación:
class S {
- Usar tuplas paravoid
public devolver
GetAMvalores
() { múltiples
return this.a * 12;
}

private int a;
}
// readable version
class Student {
public const int MonthsInYear = 12;
public void GetAgeInMonths () {
return this.age * MonthsInYear;
}

private int age;


}
Las tuplas son una forma efectiva y simple de devolver múltiples valores de
diferentes tipos, en lugar de usar una clase separada (a veces llamada POCO, como
"Objeto C # antiguo").
Echar un vistazo:
- Construir cadenas como un profesional
class User {
public Tuple<int, string, string> GetUserData () {
return Tuple.Create(this.userId, this.fullName, this.email);
}

private int userId;


private string email;
private string fullName;
}

Las cuerdas se usan en casi todas partes. Puede parecer no tan obvio, pero las
operaciones de cadena no son tan buenas cuando se trata de rendimiento.
Aquí hay cuatro formas de construir cadenas en C #:

Lista string str;

// version 1
str = "Username: " + username + ", Email: " + email;

// version 2
str = String. Format ("Username: {0}, Email: {1}", username, email);

// version 3, string interpolation


str = $"Username: {username}, Email: {email}";

// version 4, string builder


StringBuilder builder = new StringBuilder ();
builder. Append ("Username: ");
builder. Append (username);
builder. Append (", Email: ");
builder. Append(email);
str = builder. ToString ();

Use el operador de interpolación de cadenas ($) para cadenas más pequeñas y


StringBuilder para cadenas más grandes.

vinculada en C #
Cada elemento (o nodo) en la lista vinculada tiene datos y un puntero a otro nodo. El
puntero almacena la ubicación de la memoria del siguiente nodo.
El class
último nodo de{la lista no debe apuntar a nada; debe ser un puntero nulo.
LinkedList
Comencemos classcreando
Node { la clase Node:
public int value;
public Node next;
public Node (int value) {
this. value = value;
}
}
}
El objeto de nodo contiene un campo de valor y un siguiente puntero.
Ahora podemos crear los métodos de LinkedList correspondientes:
 Ordenar burbujas en C #
class LinkedList {
void addAtFront (Node node);
void addAtEnd (Node node);
void removeFront
void bubbleSort (int [] arr) {();
void print
bool swapped;();
} for (int i = 0; i < arr.Length - 1; i++) {
swapped = false;
for (int j = 0; j < arr.Length - i - 1; j++) {
if(arr[j] > arr [j + 1]) {
int temp = arr[j];
arr[j] = arr [j + 1];
arr [j + 1] = temp;
swapped = true;
}
}
if (swapped == false)
break;
}
}
}

El intercambio booleano se usa para determinar si el intercambio ocurrió durante una


iteración. Si no sucedió ningún intercambio, la matriz se ordena y podemos romper el
ciclo.

La clasificación de burbujas se utiliza para ordenar una lista de elementos, comparando


dos elementos adyacentes e intercambiándolos, si no están en orden:
 Pila en C #
Implementemos una estructura de pila usando una lista vinculada.
Recuerde, cada nodo de una lista vinculada almacena la dirección del siguiente nodo.
Para implementar una pila, primero debemos definir la clase Node:

class Node {
public int value;
public Node next;
public Node (int value) {
this.value = value;
class Stack
} {
} class Node {
public int value;
public Node next;
Cada nodo tiene un valorNode
public y el siguiente
(int value)puntero.
{ Ahora, podemos implementar la clase
Stack con sus correspondientes métodos push,
this.value = value; pop e print:
 Matriz de}adyacencia en C #
}
private Node first = null;

void push (Node node);


void pop ();
void print ();
}
Una matriz de adyacencia es una matriz cuadrada utilizada para representar un gráfico
finito. Los elementos de la matriz indican si los pares de vértices son adyacentes o no,
usaremos el valor 1 para vértices adyacentes, 0 para los no vértices.
Si el gráfico no está dirigido, la matriz de adyacencia es simétrica.
Nuestra clase tendrá la siguiente estructura:

class AdjacencyMatrix {
public class Graph {
int V;
public int [,] adjMatrix;

public Graph (int V) {


this.V = V;
adjMatrix = new int [V, V];
}
public void addEdge (int src, int dest);
public void removeEdge (int src, int dest);
public void print ();
}
}

Almacenamos la matriz en una matriz bidimensional, llamada adjMatriz. Creamos una


clase Graph donde definimos los métodos addEdge (), removeEdge () e print ()
correspondientes.
 Transposición matricial en C #
La transposición de una matriz m por n es una matriz n por m. La transposición
intercambia las filas y las columnas de una matriz.
Definamos una función de transposición, que tomará una matriz bidimensional y
devolverá su transposición:

 Árbol binario en C #
int [,] transpose (int [,] a) {
int [,] res = new int[a.GetLength(1), a.GetLength(0)];
for (int k = 0; k < a.GetLength(0); k++)
for (int j = 0; j < a.GetLength(1); j++)
res[j,k] = a[k,j];

return res;
}

Unclass
árbol binario
Node { es una estructura de datos en la que cada nodo tiene como máximo dos
hijos. public int data;
Para implementar un árbol
public Node binario, primero definamos una clase de nodo para
left, right;
public
representar Node
un solo (int en
nodo item) {
el árbol. Cada nodo puede tener nodos izquierdo y derecho,
data punteros
por lo que tendremos = item; izquierdo y derecho en la clase:
left = null;
right = null;
}
}
Ahora podemos comenzar a crear nuestro árbol:
 Búsqueda binaria en C #
Binary Search está buscando en la matriz ordenada dividiendo repetidamente el intervalo
de búsqueda por la mitad.
Podemos usar la recursión para implementar el algoritmo. Durante cada iteración,
tomamos el elemento del medio, lo comparamos con el valor buscado, luego tomamos la
mitad correspondiente y repetimos el proceso.
Nuestra función buscará el valor x en la matriz arr:

int binarySearch (int [] arr, int l, int r, int x) {


if (r >= l) {
int mid = l + (r - l) / 2;
class BinaryTreeif{(arr[mid] == x)
class Node { return mid;
public
if int data;
(arr[mid] > x)
public Nodereturnleft, right;
binarySearch (arr, l, mid - 1, x);
public
else Node (int item) {
data = item;
return binarySearch (arr, mid + 1, r, x);
} left = null;
return -1; right = null;
} }
}
Node root;
BinaryTree () {
root = null;
}
void insert (int data);
}

Hemos utilizado la recursión para implementar el método de inserción y seleccionamos


al azar el lado donde se debe insertar el nuevo nodo.

Devolvemos -1, si no se encuentra el elemento.


 Multiplicación matricial en C #
El resultado de una matriz m por n multiplicado por una matriz n por k da como resultado
una matriz m por k.
Cada elemento de esa matriz es el producto escalar de cada fila correspondiente de la
int [,] mult
primera (inty [,]
matriz a, int [,] b)
la columna {
correspondiente de la segunda matriz.
int rows = a.GetLength(0);
Definamos una función mult, que tomará las dos matrices como argumentos y devolverá
int size = b.GetLength(0);
su multiplicación:
int cols = b.GetLength(1);
int [,] res = new int[rows,cols];

for (int k = 0; k < rows; k++) {


for (int j = 0; j < cols; j++) {
int ret = 0;
for (int x = 0; x < size; x++) {
ret += a[k,x] * b[x,j];
}
res[k,j] = ret;
}
}
return res;
}
El código anterior itera a través de los elementos de las matrices y calcula sus productos
de punto correspondientes en la matriz res.
 QuickSort en C #
QuickSort elige un elemento como pivote y divide la matriz dada a su alrededor.
Elegiremos nuestro pivote al azar.
Implementaremos la función QuickSort de forma recursiva. Durante cada iteración,
nuestra función elegirá un pivote aleatorio y dividirá la matriz en función de ello:

void QuickSort (int [] arr, int left, int right) {


int l = left;
int r = right - 1;
int size = right - left;
if (size > 1) {
Random rand = new Random ();
int pivot = arr [rand.Next(0, size) + l];
while (l < r) {
while (arr [r] > pivot && r > l) {
r--;
}
while (arr [l] < pivot && l <= r) {
l++;
}
if (l < r) {
int temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
l++;
}
}
QuickSort (arr, left, l);
QuickSort (arr, r, right);
}
}

Entonces, la función QuickSort está tomando la matriz de elementos, eligiendo un punto


de pivote aleatorio, crea dos particiones basadas en ese pivote y lo llama recursivamente
hasta que todos los elementos se ordenan.
 Cola en C #
Implementemos una estructura de cola usando una lista vinculada.
Recuerde, cada nodo de una lista vinculada almacena la dirección del siguiente nodo.
Para implementar una cola, primero debemos definir la clase Node:

class Node {
public int value;
public Node next;
public Node (int value) {
this.value = value;
}
}
Cada nodo almacena un valor y un siguiente puntero.
Ahora, podemos implementar la clase Queue con sus correspondientes métodos de
enqueue, dequeue e print:

 Agregar matrices en C #
class Queue {
class Node {
public int value;
public Node next;
public Node (int value) {
this.value = value;
}
}
private Node first = null;
private Node last = null;
void enqueue (Node node);
void dequeue ();
void print ();
}

Considere dos matrices, que están representadas por las matrices bidimensionales ay b.
Para agregarlos juntos, necesitamos iterar a través de todos los elementos de las
matrices, agregar los elementos correspondientes y almacenar los valores en una matriz
res.

int [,] add (int [,] a, int [,] b) {


int [,] res = new int[a.GetLength(0), a.GetLength(1)];
for (int k = 0; k < a.GetLength(0); k++)
for (int j = 0; j < a.GetLength(1); j++)
res[k,j] = a[k,j] + b[k,j];

return res;
}

El método add toma dos matrices, a, b como argumentos. Dos bucles iteran a través de
sus elementos y almacenan la suma de los elementos correspondientes de a y b en la
matriz res.
 Tabla hash en C #
Recuerde, para agregar matrices, sus tamaños deben ser los mismos.

Hash Table es una estructura de datos que asigna claves a valores. Los datos se
almacenan en un formato de matriz, donde cada valor tiene su propia clave única.
Vamos a definir una clase HashEntry para nuestros elementos. Cada elemento tiene una
propiedad de datos de cadena.

class HashEntry {
public String value;
public HashEntry (String value) {
this.value = value;
}
}
Ahora podemos definir nuestra clase HashMap, que implementará la lógica de la tabla
hash.
Utilizaremos un algoritmo de hash simple, que calculará la suma de caracteres de la
cadena y la modulará según el tamaño de la tabla. Nuestros datos se almacenarán en una
matriz simple llamada tabla.
Aquí está la estructura de HashMap:

class HashMap {
int TABLE_SIZE = 127;
public HashEntry [] table;

public HashMap () {
table = new HashEntry [TABLE_SIZE];
for (int k = 0; k < TABLE_SIZE; k++)
table[k] = null;
}
int HashFunc (String value);
public void insert (String value);
public HashEntry search (String value);
public void remove (String value);
}

Este algoritmo de hashing es solo un ejemplo simple. Las funciones de hash del mundo
real son más complejas porque, por ejemplo, estas dos cadenas generarán el mismo hash
y provocarán una colisión ("ABC" y "CBA" tienen la misma clave).
 Árbol de búsqueda binaria en C #
BST es un árbol binario en el que la clave en cada nodo es mayor o igual que cualquier
clave almacenada en el subárbol izquierdo, y menor o igual que cualquier clave
almacenada en el subárbol derecho.
Podemos usar la recursividad para implementar la operación de búsqueda:
 Ordenación por inserción en C #
Node search (Node root, int data) {
if (root == null) {
Console.Write("Not found\n");
return null;
}
if (root.data == data) {
Console.Write("Found at: " + root + "\n");
return root;
}
if (root.data < data)
return search (root.right, data);
else
return search (root.left, data);
}

La ordenación por inserción se utiliza para ordenar una lista de elementos, tomando cada
elemento e insertándolo en la posición correspondiente a la izquierda.

void InsertionSort (int [] arr) {
int j;
for (int i=1; i<arr.Length; i++) {
j = i;
while(j>0 && arr [j-1] > arr[j]) {
//swap
int temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
j = j-1;
}
}
}

Pasamos por cada elemento de la matriz, comenzando por el segundo. Para cada
elemento lo intercambiamos con su vecino izquierdo, hasta que esté en su posición
correspondiente.

Selección Ordenar en C #
La ordenación por selección se utiliza para ordenar una lista de elementos. Durante cada
iteración, el algoritmo toma el elemento más pequeño y lo intercambia con el elemento
en la posición correspondiente a la izquierda.
Nuestro método tomará una matriz llamada arr como parámetro y la ordenará:

void SelectionSort (int [] arr) {


for (int k = 0; k < arr.Length-1; k++) {
int min_idx = k;
for (int j = k+1; j < arr.Length; j++)
if (arr[j] < arr[min_idx])
min_idx = j;
//swap
int temp = arr[min_idx];
arr[min_idx] = arr[k];
arr[k] = temp;
}
}

Durante cada iteración se encuentra el índice del elemento más pequeño y se intercambia
con la posición correspondiente.
 Búsqueda lineal en C #
La búsqueda lineal es un algoritmo de búsqueda simple que verifica la coincidencia de
cada elemento de la matriz y devuelve el índice del elemento coincidente.
La implementación es muy simple: solo necesitamos un bucle para recorrer toda la matriz
y buscar una coincidencia.
Busquemos el valor x en una matriz llamada arr:

int search (int [] arr, int x) {


for (int k = 0; k < arr.Length; k++) {
if (arr[k] == x)
return k;
}
return -1;
}

Realizamos iteraciones a través de la matriz utilizando un bucle for y buscamos una


coincidencia. Si el elemento actual es igual a x, se devuelve el índice de ese elemento.
 Combinar Ordenar en C #
-1 se devuelve si no se encuentra ninguna coincidencia.

Merge Sort está dividiendo la matriz por la mitad una y otra vez hasta que cada pieza
tenga solo un elemento. Luego, esos elementos se vuelven a unir (fusionar) en orden de
clasificación.
Necesitaremos una función Merge, para fusionar las dos mitades en datos ordenados.
Implementemos el algoritmo usando recursividad:

void MergeSort (int [] arr, int l, int r) {


if (l < r) {
int m = (l+r)/2;
MergeSort (arr, l, m);
MergeSort (arr, m+1, r);
merge (arr, l, m, r);
}
}

Estamos tomando la matriz, la dividimos en dos partes y continuamos este proceso hasta
que cada elemento se convierta en un solo elemento. Luego comenzamos a fusionarlos
de nuevo de manera ordenada para obtener el resultado.
La función de fusión es la pieza más importante de esta implementación.

Summary
/// <summary> //Para documentar un metodo
/// Metodo principal //Descripcion del metodo
/// </summary>
/// <param name="args">Descripcion del parametro args</param>
Region
#region Imprimir //Sirve para separar bloques de codigos
Console.WriteLine("Probando mi primer proyecto"); //Instruccion para
mostrar un mensaje a traves de la consola
#endregion
Tipos de datos
int positivo = 500;
int negativo = -800;
float minumero = 530.60f;
double midouble = 250.50;
decimal midecimal = 835.28m; //para calculos monetarios o financieros
byte diasSemana = 7;
bool miBandera = true;

string miCadena = "Probando string";


char michar = miCadena[0];
*/ç

/*Tipos de datos nullables


bool? miBandera = null;
int? miNumero = null;

Console.ReadKey(); //Instruccion para que no se cierre la ventana de la


consola despues de mostrar un mensaje, para cerrar presionar cualquier
tecla

También podría gustarte