Curso C#
Curso C#
Curso C#
Conceptos Básicos
¿Qué es C#?
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
Estos conceptos pueden parecer complejos, pero por ahora, solo recuerda que las
aplicaciones escritas en C# utilizan el Framework .NET y sus componentes
Variables
Para utilizar una variable, primero debe ser declarada especificando el nombre y el tipo de
dato.
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.
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.
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:
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:
Imprimir Texto
- Mostrar salida
int x = 89;
// Outputs 89
Podemos mostrar valores variables en la ventana de la consola:
Para mostrar una cadena de texto formateada, use la siguiente sintaxis:
int x = 10;
double y = 20;
// Output: x = 10; y = 20
Puede tener tantos marcadores de posición de variables como necesite. (es decir: {3},
{4}, etc.).
Obteniendo la entrada del usuario
- Entrada del usuario
string yourName ;
Tenga en cuenta los paréntesis vacíos en el método ReadLine. Esto significa que no
toma ningún argumento.
/ * 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.
var num;
num = 42;
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:
- División
int x = 10 / 4;
Console.WriteLine (x);
// Outputs 2
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
// Outputs 4
porque devuelve el resto de una división entera.
Por ejemplo:
- Precedencia del operador
int x = 4+3*2;
Console.WriteLine (x);
// Outputs 10
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.
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:
int x = 10;
x++;
++x; // prefijo
Console.WriteLine
x++; // sufijo (x);
// Outputs 11
int x = 3;
int y = ++x;
// x es 4, y es 4
int x = 3;
int y = x++;
// x es 4, y es 3
Ejemplo de sufijo:
- Operador de decremento
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");
}
}
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:
- 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"
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.
- 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
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.
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:
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:
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
Con el operador AND, ambos operandos deben ser verdaderos para que toda la
expresión sea verdadera.
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”);
}
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.
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:
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());
"exit".
Si el usuario ingresa "exit" como el valor de x, el programa debe salir del bucle. Para hacer
Console.WriteLine(“y =”);
int y = Convert.ToInt32 (Console.ReadLine());
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
- -
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.
La palabra clave static se discutirá más adelante; se usa para hacer que los métodos
sean accesibles en Main.
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:
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.
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:
El método Sum toma dos enteros y devuelve su suma. Es por esto que el tipo de retorno
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.
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.
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 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:
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.
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.
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);
}
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:
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.
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
}
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");
}
}
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
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;}
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;
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
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
- 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:
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
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:
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.
Cada dimensión es un arreglo, por lo que también puedes inicializar el arreglo durante la
Console.WriteLine(arr.Rank);
//Outputs 1
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
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
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.
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.
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:
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.
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
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.
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}
DateTime.DaysInMonth(2016, 2);
//return the number of days in the specified month
class Person {
private string name;
public Person (string name) {
this.name = name;
}
}
class Person {
private readonly string name = "John";
public Person (string name) {
this.name = name;
}
}
Los arreglos utilizan índices enteros, pero los indexadores pueden utilizar cualquier tipo
de índice, como strings, caracteres, etc.
class Clients {
private string [] names = new string [10];
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"
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
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;
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:
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");
}
}
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.
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.
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");
}
}
Por lo tanto, ¿qué pasa cuando nosotros creamos un objeto de la clase derivada?
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.
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");
}
}
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:
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.
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 ():
Una interfaz es una clase completamente abstracta, la cual contiene solamente miembros
abstractos.
Estas son declaradas utilizando la palabra clave interface:
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:
Fíjate que la palabra clave override no es requerida cuando estás implementando una
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) {
}
}
}
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.
Console.WriteLine(b.title);
//Outputs "Test"
}
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:
Por ejemplo, el siguiente código producirá una excepción cuando sea ejecutado porque
solicitamos un índice el cual no existe:
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.
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");
}
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
Para utilizar la clase File necesita utilizar el namespace System.IO: using System.IO;
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:
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.
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:
string x = "Hello";
string y = "World";
Swap<string> (ref x, ref y);
//Now x is "World", y is "Hello"
}
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:
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
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>:
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] + " ");
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>:
ba1.SetAll(true);
ba2.SetAll(false);
ba1.Set(2, false);
ba2.Set(3, true);
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>:
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("\nStack: ");
foreach (int i in s)
Console.Write(i + " "); // 72 59
Console.Write("\nCount: " + s.Count); // 2
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>:
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("\nQueue: ");
foreach (int i in q)
Console.Write(i + " "); // 10 15
Console.Write("\nCount: " + q.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.
private int a;
}
// readable version
class Student {
public const int MonthsInYear = 12;
public void GetAgeInMonths () {
return this.age * MonthsInYear;
}
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);
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;
}
}
}
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;
class AdjacencyMatrix {
public class Graph {
int V;
public int [,] adjMatrix;
Á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:
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.
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á:
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:
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:
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;