Está en la página 1de 31

The if statement

En cada lenguaje de programacion uno de los comandos mas importantes es el if. Tener la
habilidad de establecer bloques condicionales de código en uno de los principios fundamentales
de desarrollar software. En C#, el uso del comando if es muy simple. Si tienes experiencia en otro
lenguaje de programacion, es probable que puedas utilizar el comando if de inmediato. En
cualquier otro caso, lee para entender como utilizarlo. El comando if requiere de un resultado del
tipo Boolean, es decir, true (Verdadero) o false (Falso). En algunos lenguajes de programacion,
muchos tipos de datos pueden ser transformados automaticamente en Booleans, sin embargo en
C# necesita hacerlo especificamente. De hecho, no se puede utilizar if(numero), debe comparer el
numero con algo para generar un resultado true (Verdadero) o false (Falso), tal como veremos mas
adelante.

En el capitulo anterior le dimos un vistazo a las variables, ahora queremos ver uno de los ejemplos
de como las condiciones lógicas pueden ser usadas

using System;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int number;

Console.WriteLine("Please enter a number between 0 and 10:");


number = int.Parse(Console.ReadLine());

if(number > 10)


Console.WriteLine("Hey! The number should be 10 or less!");
else
if(number < 0)
Console.WriteLine("Hey! The number should be 0 or more!");
else
Console.WriteLine("Good job!");

Console.ReadLine();
}
}
}

Utilizaremos dos comandos if para validar que un numero ingresado este en el rango entre 0 y 10,
introduciremos la clausula else. La clausula else debe ser obvia para cualquiera que hable ingles.
Simplemente ofrece una alternativa para decidir que codigo debe ser ejecutado en caso el
condicion evaluada en el comando if no se cumpla.

Tal como se observa, no utilizamos las llaves { } para definir el bloque condicional del codigo. La
regla indica que si el bloque contiene una sola linea de codigo entonces las llaves { } no son
necesarias. Ahora bien, el ejemplo anterior parece que está usando demasiadas lineas de codigo
para comparar simplemente un número, ¿verdad? Definitivamente, se puede implementar con
menos líneas como lo siguiente:

if((number > 10) || (number < 0))


Console.WriteLine("Hey! The number should be 0 or more and 10 or less!");
else
Console.WriteLine("Good job!");

Colocamos cada condición encerrada entre paréntesis, luego usamos el operador || , el cual
significa "o", para validar si el numero es mayor que 10 "O" menor que cero. Otro operador muy
frecuentemente utilizado es AND (y) representado por &&. podríamos haber utilizado el operador
AND ? Por supuesto que si, se necesitaría un simple ajuste en el código tal como lo siguiente:

if((number <= 10) && (number >= 0))


Console.WriteLine("Good job!");
else
Console.WriteLine("Hey! The number should be 0 or more and 10 or less!");

El código de arriba nos presenta dos nuevos operadores: <= (menor que o igual que) y >= (mayor
que o igual que).

The switch statement

La declaración "switch" es como una serie de declaraciones "if". Es una lista de posibilidades, con
una acción para cada posibilidad, y una acción opcional por defecto, en caso de que nada se
evalúe como verdadero. Una simple declaración "switch" se ve de la siguiente forma:

int number = 1;
switch(number)
{
case 0:
Console.WriteLine("The number is zero!");
break;
case 1:
Console.WriteLine("The number is one!");
break;
}

El identificador por evaluar es colocado después de la palabra clave "switch", y luego está la lista
de los casos declarados, en donde evaluaremos el identificador contra los valores dados. Te darás
cuenta de que tenemos una declaración "break" al final de cada caso. C# simplemente requiere
que dejemos el bloque antes de que este termine. En caso de que estuvieses escribiendo Una
función, pudieses usar una declaración "return" en lugar de una declaración "break".

En este caso usamos un "integer", pero pudiese haberse usado también un "string", o cualquier
otro tipo. También puedes especificar la misma acción para múltiples casos. Lo haremos en el
próximo ejemplo también, donde tomaremos una entrada (input) del usuario y la usaremos en
nuestra declaración "switch":

Console.WriteLine("Do you enjoy C# ? (yes/no/maybe)");


string input = Console.ReadLine();
switch(input.ToLower())
{
case "yes":
case "maybe":
Console.WriteLine("Great!");
break;
case "no":
Console.WriteLine("Too bad!");
break;
}

En este ejemplo, le hacemos una pregunta al usuario y le sugerimos que introduzca un si, no, o
puede ser (yes/no/maybe). Entonces leemos la entrada del usuario, y creamos una declaración
"switch" para ella. Para ayudar al usuario, convertimos la entrada a minúsculas antes de compararla
con nuestras cadenas de texto (strings) en minusculas, de manera de que no haya diferencia entre
letras minúsculas y mayúsculas.

De todas maneras, el usuario puede cometer un error ortográfico o tratar de escribir algo
completamente diferente; y en ese caso, no se generaría ninguna salida (output) por esta
declaración "switch". Introduce la palabra clave "default"

Console.WriteLine("Do you enjoy C# ? (yes/no/maybe)");


string input = Console.ReadLine();
switch(input.ToLower())
{
case "yes":
case "maybe":
Console.WriteLine("Great!");
break;
case "no":
Console.WriteLine("Too bad!");
break;
default:
Console.WriteLine("I'm sorry, I don't understand that!");
break;
}

Si ninguno de los casos da como resultado verdadero, entonces la declaración "default"; en caso
de haberla, será ejecutada. Esto es opcional, como pudimos ver en los ejemplos previos.

Loops

Otra técnica esencial al momento de escribir código es el Looping o bucle (la habilidad de repetir
un bloque de código X veces). En C#, estos vienen 4 variantes distintas, y veremos a cada una de
estas.

El ciclo while es probablemente uno de los más simples, así que empezaremos con este. Este ciclo
simplemente ejecuta un bloque de código durante el tiempo que la condición descrita se cumpla.
A continuación un pequeño ejemplo y después una explicación:

using System;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int number = 0;

while(number < 5)
{
Console.WriteLine(number);
number = number + 1;
}
Console.ReadLine();
}
}
}

Intenta correr el código. Obtendrás una lista de números del 0 al 4. El número es definido primero
como 0 y cada vez que el código en el Loop es ejecutado este se incrementará por uno. Pero ¿Por
qué el número solo llega a 4 cuando en el código dice 5? Pues, para que la condición se cumpla y
regrese el valor, el número debe ser menor a 5. En este caso, una vez que el número es igual a 5, la
línea de código que regresa o entrega el valor del número nunca es ejecutada, debido a que la
condición del Loop While es evaluada antes de entrar a ejecutar el bloque de código.

El ciclo do

Lo contrario sucede con el ciclo do, que opera parecido al ciclo while en algunos aspectos. Este
ciclo evalúa la condición después de que el bloque de código ha sido ejecutado, lo que asegura
que el bloque sea ejecutado al menos una vez.

int number = 0;
do
{
Console.WriteLine(number);
number = number + 1;
} while(number < 5);

El resultado sería el mismo - una vez que el número sea mayor a 5, el ciclo es abandonado.

El ciclo for

El ciclo for es un poco diferente. Es preferido cuando tu sabes cuántas iteraciones quieres, porque
sabes la cantidad exacta de iteraciones, o porque tienes una variable conteniendo la cantidad. Aquí
hay un ejemplo de un ciclo for.

using System;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int number = 5;

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


Console.WriteLine(i);

Console.ReadLine();
}
}
}

Esto produce el mismo resultado, pero como puedes ver, el ciclo for es un poco más compacto.
Consiste de 3 partes - inicializamos una varaible para contar, creamos una sentencia condicional
para probarlo, e incrementamos el contador (++ significa lo mismo que "variable = variable + 1").
La primera parte, en donde definimos la variable i y la iniciamos en 0, se ejecuta una sola vez, antes
de que el ciclo empiece. Las últimas dos partes son ejecutadas en cada iteración del ciclo. Cada
vez, i es comparada con nuestra variable 'number' - si i es más pequeña que 'number', el ciclo se
ejecuta una vez más. Después de eso, i es incrementada en 1.

Intenta correr el programa y después, intenta cambiando la variable 'number' a algo más grande o
más pequeño que 5. Vas a ver que el ciclo responde al cambio.

El ciclo foreach

El último ciclo que veremos, es el ciclo foreach. Opera en colecciones, como arreglos u otro tipo de
listas integradas. En nuestro ejemplo, vamos a usar una de las listas más simples, llamada ArrayList.
Funciona casi igual que un arreglo, pero no te preocupes, lo veremos en otro capítulo.

using System;
using System.Collections;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
ArrayList list = new ArrayList();
list.Add("John Doe");
list.Add("Jane Doe");
list.Add("Someone Else");

foreach(string name in list)


Console.WriteLine(name);

Console.ReadLine();
}
}
}

Bien, creamos una instancia de un ArrayList y luego le añadimos algunos elementos de tipo
cadena. Usamos el ciclo foreach para ir por cada elemento, asignando la variable 'name' al
elemento en el que estamos en cada ocasión. De esa manera, tenemos una variable nombrada
para dar salida. Como puedes ver, declaramos la variable 'name' de tipo string - siempre debes
decirle al ciclo foreach qué tipo de datos esperas obtener de la colección. En caso de que tengas
una lista de varios tipos, debes usar la clase 'object' en lugar de una clase específica, para sacar
cada elemento como un objeto.

Cuando trabajas con colecciones, es muy probable que uses el ciclo foreach la mayor parte del
tiempo, principalmente porque es más simple que cualquier otro ciclo para esta clase de
operaciones.

Introduction to C# classes

En muchos tutoriales de programación, la información sobre las clases es guardada para mucho
después. Sin embargo, ya que C# trata sobre la programación orientada a objetos y como
consecuente también las clases, ahora daremos un vistazo a una básica introducción hacia los
detalles más importantes.
Primero que todo, una clase es un grupo de métodos y variables relacionados. Una clase describe
estos conceptos, y en la mayoría de los casos, se crea una instancia de esta clase, esto se conoce
como objeto. En este objeto, se pueden usar los métodos y las variables definidas. Por supuesto,
se pueden crear todas las instancias que se quieran en la clase. Las Clases y la Programación
Orientada a Objetos en general es un tema extenso. Cubriremos algunos de estos temas en este
capítulo y en capítulos posteriores, pero no todo el tema.

En el capitulo "Hello World", vimos una clase por primera vez, ya que todo en C# esta construido
sobre clases. Expandamos nuestro Hola Mundo con una clase que nosotros construiremos:

using System;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Car car;

car = new Car("Red");


Console.WriteLine(car.Describe());

car = new Car("Green");


Console.WriteLine(car.Describe());

Console.ReadLine();

}
}

class Car
{
private string color;

public Car(string color)


{
this.color = color;
}

public string Describe()


{
return "This car is " + Color;
}

public string Color


{
get { return color; }
set { color = value; }
}
}
}

Bueno, hay muchas cosas nuevas aquí, pero la mayoría se basan en cosas que ya hemos utilizado
anteriormente en este tutorial. Como puede ver, hemos definido una nueva clase, llamada Car. Se
declaró en el mismo archivo que nuestra aplicación principal, para una descripcion mas fácil, sin
embargo, generalmente las nuevas clases se definen en sus propios archivos. Se define una única
variable, llamada color, que por supuesto se utiliza para determinar el color de nuestro car. La
declaramos como privada, lo que es una buena práctica - el acceso a las variables desde el exterior
se debe hacer utilizando una propiedad. La propiedad Color se define al final de la clase, dando
acceso a la variable de color.

Además de eso, nuestra clase Car define un constructor. Toma un parametro el cual nos permite
inicializar objetos tipo Car con un color. Debido a que solo hay un constructor, los objetos tipo Car
solo ser pueden instanciar con un color. El metodo Describe() nos permite obtener un nice
mensaje con una unica pieza de informacion that acerca de nuestro car. Simplemente devuelve
una cadena con la información que proporcionamos.

Ahora, en nuestra aplicación principal, declaramos una variable de tipo Car. Después de eso,
hemos creado una nueva instancia de ella, con el color "Rojo" como parámetro. De acuerdo al
código de nuestra clase, significa que el color rojo sera asignado como el color de Car. Para
verificar esto, nosotros llamamos al método Describe(), y para mostrar cuan fácilmente podemos
crear varias instancias de la misma clase, lo hacemos una vez mas, pero con otro color. Y así hemos
creado nuestra primera clase funcional y la hemos usado.

En los siguientes capitulos, conceptos como: propiedades, constructores y visibilidad seran


explicados en profundidad.

Clases: Fields

Uno de los bloques de construcción más básicos es un campo. Es como una variable, como
hablamos previamente, pero definido en el nivel de clase en lugar de en el nivel método. La
diferencia es bastante importante y tiene que ver con el concepto de alcances, que decide desde
dónde puede ser accedida una variable: Una variable local (definida dentro de un método) solo
puede ser accedida desde este método específico, mientras que un campo de clase puede ser
accedido desde todos los métodos de una clase e incluso desde métodos de otras clases si la
visibilidad lo permite.

En otras palabras, la diferencia entre una variable y un campo es más o menos dónde se declara.
Una variable a nivel de clase se conoce como un campo, mientras que una variable a nivel de
método generalmente se conoce como variable.

Los campos a menudo se declaran cerca de la parte superior de la clase y su visibilidad a menudo
se establecen en privado (discutiremos la visibilidad más adelante en este capítulo). Podría verse
así:

public class FieldsSample


{
private string name;
....

Ahora tenemos una variable de nivel de clase llamada "name", a la que se puede acceder desde
todos los métodos de esta clase. No se puede acceder desde fuera de la clase, pero solo porque lo
hemos marcado como privado(private). Usted es libre de declarar sus campos
como protegido(protected) si desea acceder a ellos desde clases derivadas o
incluso público(public) si desea acceder a ellos desde cualquier lugar, pero tenga en cuenta que
la forma recomendada de acceder a los campos de fuera de una clase es a través de propiedades,
que discutiremos en el siguiente artículo.
En nuestro ejemplo anterior, nuestra variable "name" no tiene un valor inicial, lo que significa que
tendrá que asignarle algo antes de poder usarlo. Si ya sabe con qué valor debe comenzar su
campo, puede asignarlo fácilmente al mismo tiempo que lo declare:

public class FieldsSample


{
private string name = "John Doe";
....

Como alternativa, podemos asignar valores al campo en el constructor de la clase. Esto


sobreescribirá cualquier valor asignado al campo en la declaración y nos permitirá la asignación de
valores dinámicos.

Como hemos hablado anteriormente, los miembros de una clase pueden ser accedidos con la
notación punto, como "class.member". Sin embargo, cuando accedemos a un miembro de la clase
actual, usamos la palabra clave "this", como "this.member". Aquí tenemos un ejemplo más claro
donde declaramos un par de campos y los usamos dentro de un método.

public class Fields1


{
private string name = "John Doe";
private int age = 42;

public void Describe()


{
string description = this.name + " is " + this.age + " years old...";
Console.WriteLine(description);
}
}

En el método Describe() [Describir()] (no te preocupes, hablaremos de los métodos en uno de los
siguientes artículos), vamos a declarar una variable local llamada "description" [descripción], con
un valor basado en dos campos declarados. La variable "description" [descripción] es un ejemplo
genial de como una variable debe ser siempre una variable y nunca un campo: es temporal y solo
es relevante para el método que la usa, donde el campo puede perfectamente ser relevante en
otros métodos de la clase.

Resumen

Los campos actúan parecido a una variable global, debido a que pueden usarse para almacenar
información y ser accedidos desde cualquier parte dentro de la clase. También pueden ser
accedidos desde fuera de la clase que las define, pero normalmente, son las propiedades las que
se usan para ese propósito. Hablaremos de las propiedades en el siguiente artículo.
Clases:
Properties

En el artículo anterior, vimos los campos. Son como las variables globales de una clase, y permiten
acceder a ellos desde todos los métodos. También vimos que, de hecho, los campos PUEDEN
accederse desde otras clases si se los define como públicos, pero eso, en general, no se
recomienda. Para las variables o campos que se quieran acceder desde fuera de la clase, en
cambio se deberían usar propiedades.

Cuando usted declara un campo como público, está dando acceso completo a el desde afuera -
otras clases pueden hacer lo que quieran con él, sin notificarlo la clase declarante. Las propiedades
regresan el control a la clase declarante, al especificar si un campo es de solo lectura o escritura y
aun permitiendo a la clase declarante verificar y manipular el valor antes de retornarlo o asignarlo
al campo.

Una propiedad se ve como una mezcla entre un campo y un método, ya que es declarado mas
como un campo con visibilidad, un tipo de dato y un nombre, pero tiene también un cuerpo como
un método, para controlar el comportamiento:

public string Name


{
get { return _name; }
set { _name = value; }
}

Note las palabras clave especiales get y set. Ellas son usadas exclusivamente para propiedades,
para controlar el comportamiento cuando el campo es leido (get) y escrito (set). Usted puede
declarar propiedades con sólo una implementación de get O de un set, por ejemplo, para crear
una propiedad la cual pueda ser leida desde cualquier parte (pública) pero sólo ser modificada
desde dentro de la clase declarante (privada).

También notará que me refiero a un campo llamado _name. Usted tendrá que declarar este en la
clase también, de tal forma que su propiedad pueda usarla. Un patrón de uso común para campos
y propiedades se verá como este:

private string _name = "John Doe";

public string Name


{
get { return _name; }
set { _name = value; }
}

Ahora puede ver como el campo y la propiedad trabajan juntos: El método get regresará el valor
del campo _name, mientras que el método set asignará el valor pasado al campo _name. En el
método set, usamos la palabra clave especial value la cual, en esta situación específica, referirá al
valor pasado a la propiedad.

Así, esto es prácticamente todo lo básico como puede ser y en este punto, no hicimos nada que
no pueda ser obtenido con un simple campo público. Pero en un punto posterior, usted pudiera
decidir que quiere tomar un mayor control de como otras clases puede trabajar con el nombre y
ya que usted ha implementado esto como un propiedad, es libre de modificar la implementación
sin disturbios para quien use su clase. Por ejemplo, la propiedad Name puede ser modificada para
verse como esto:

private string _name = "John Doe";

public string Name


{
get
{
return _name.ToUpper();
}
set
{
if(!value.Contains(" "))
throw new Exception("Please specify both first and last name!");
_name = value;
}
}

El método get ahora forza a que el valor retornado sea siempre en MAYÚSCULAS, no importa en
que tipo esté el campo de respaldo (_name). En el método set, hemos agregado un par de líneas
de código para verificar si el valor pasado contiene un espacio en blanco, porque hemos decidido
que el nombre siempre debe consistir de ambos un nombre y un apellido - si este no es el caso,
una excepción es lanzada. Esto es todo muy crudo y simplificado, pero debe ilustrar el nivel
completo de control que usted obtiene cuando usa propiedades.

Propiedades Solo lectura

La mayoría de las propiedades que verá en los ejemplos de este tutorial serán tanto de lectura
como de escritura, porque ese es el uso más común de las propiedades, pero no siempre es así. En
primer lugar, puede declarar una propiedad usando solo el método get, por ejemplo:

private string _name = "John Doe";

public string Name


{
get { return _name; }
}

En este caso, ya no puede cambiar la propiedad "Name": solo puede leerlo y el compilador
arrojará un error si intenta asignarle un valor. Sin embargo, aún puede cambiar su valor desde
dentro de la clase, ya que simplemente puede asignar un nuevo valor al campo de respaldo
"_name". Sin embargo, hacerlo de esa manera niega una de las mayores ventajas de las
propiedades: la capacidad de controlar siempre si se puede aceptar un valor. Como ya
mencionamos, el método set es una excelente manera de realizar la validación del valor, excepto si
asigna un nuevo valor al campo _name desde varios lugares, debido a que la propiedad es de solo
lectura, no obtiene esta validación

Afortunadamente para nosotros, C# ofrece una solución a esto: puede definir un método set en la
propiedad, pero limitar su visibilidad, usando, por ejemplo la palabra clave private o protected.
Esto le dará lo mejor de ambos mundos, donde aún puede asignar un valor a la propiedad desde
dentro de la clase (o cualquier clase heredada si usa la palabra clave protected) y validarlo en
consecuencia. Aquí hay un ejemplo:
private string _name = "John Doe";

public string Name


{
get { return _name; }

private set
{
if(IsValidName(value))
this._name = value;
}
}

public bool IsValidName(string name)


{
return name.EndsWith("Doe");

La diferencia clave aquí es simplemente la palabra clave "privada" justo en frente de la palabra
clave "set" y, como se mencionó, puede reemplazarla con, por ejemplo, protected o internal, según
sus necesidades.

Propiedades Auto-implementadas

En algunos casos, no necesita todo el control sobre los campos y se puede sentir incómodo de
implementar ambos un campo y un propiedad con los métodos get y set sin hacer nada adicional
a lo que vimos en el primer ejemplo. Usted puede ser tentado a declarar simplemente sus
variables como un campo público y evitar toda esta molestia. Pero no lo haga! Afortunadamente
para todos nosotros, Microsoft decidió agregar las propiedades auto-implementadas en C#
versión 3, lo cual le ahorrará varias líneas de código. Sólo considere la diferencia:

Propiedad regular con un campo de respaldo declarado:

private string _name;

public string Name


{
get { return _name; }
set { _name = value; }
}

El mismo comportamiento exacto, pero con una propiedad auto-implementada:

public string Name { get; set; }

Note que los métodos get y set están vacíos y que no se declara un campo de respaldo - en otras
palabras, ¡Ahora podemos obtener el mismo comportamiento exacto como en el primer ejemplo
pero con un sola línea de código! Tenga en mente que un campo de respaldo privado aún existirá
en el tiempo de ejecución - será auto-implementado por el compilador, como lo implica su
nombre. Más tarde podría decidir que usted necesita más control de esta propiedad específica,
usted puede simplemente cambiarla a una combinación regular de campo/propiedad con la
implementación deseada de los métodos get y set.
Note que aún tiene un mecanismo importante de control de las propiedades regulares cuando usa
propiedades auto-implementadas: Usted puede dejar fuera las palabra clave set para crear un
propiedad de sólo lectura, por ejemplo como este:

public string ReadOnlyProperty { get; }

Las propiedades de sólo escritura no son permitidas cuando se usan propiedades auto-
implementadas.

Propiedades auto-implementadas con valores default

Previo a C# versión 6, usted no podía definir un valor default (por omisión) de una propiedad
auto-implementada - para ello, usted necesitaría un campo de respaldo declarado, el cual le
permitiría inicializar la variable con un valor:

private string _name = "John Doe";

public string Name


{
get { return _name; }
set { _name = value; }
}

Pero en C# versión 6, Microsoft finalmente agregó la abilidad para inicializar una propiedad auto-
implementada con un valor default, como esta:

public string Name { get; set; } = "John Doe";

Expression-bodied properties

Otra característica relacionada con las propiedades que Microsoft implementó en C# 6.0 y 7.0 son
los miembros con cuerpo de expresión. Se permite simplemente escribir expresiones de una sola
línea para las propiedades y los métodos - en este caso, veamos cómo usarlo para tus métodos
get/set de manera que tome menos espacio y requiera escribir un poco menos.

private string name;


public string Name
{
get => name;
set => name = value;
}

Si la propiedad es de solo lectura, la sintaxis es todavía mas corta:

public string Name => "John Doe";

Por supuesto esto también funciona si necesitas hacer algo antes de retornar el valor, como ahora:

public string Name { get; set; } = "John Doe";

public string FirstName => this.Name.Substring(0, this.Name.IndexOf(" "));

Como puedes ver, esto te permite definir un metodo get, pero sin las palabras get y return,
mientras que anima a mantener todo en una sola línea en lugar de usar varias.
Resumen

Las propiedades le dan a su clase más control sobre como los campos puedes ser accedidos y
manipulados, y estas siempre deben ser usadas cuando usted quiere dar acceso a los campos
desde fuera de la clase declarante.

Clases:
Methods (functions)

Mientras que las propiedades y los campos pueden considerarse partes pasivas de una clase, los
métodos son activos. Realizarán una o varias acciones y, opcionalmente, devolverán un resultado.
En otros lenguajes de programación, a veces se les llama funciones o incluso "funcs", pero en C #,
donde pertenecen a una clase, se denominan métodos. Los métodos son muy útiles porque le
permiten encapsular una parte de la funcionalidad en un método que luego puede volver a llamar
desde varios lugares.

Un método se define así:

public int AddNumbers(int number1, int number2)


{
return number1 + number2;
}

Este método muy básico adicionará dos números y regresa el resultado. Recorramos las diferentes
partes de él:

▪ public [pública] es la visibilidad (más de esto más adelante en este tutorial).


▪ int es el tipo de dato retornado. Si quiere que su método no regrese algo, use la
palabra clave void
▪ AddNumbers es el nombre del método
▪ (int number1, int number2) - estos son los parámetros (más de esto adelan. Aunque
los paréntesis no son opcionales.
▪ Dentro del método (entre los paréntesis rizados), encontrará el código actual del
método. Puede ser una o más líneas de código.

Para llamar a un método, simplemente escriba su nombre seguido de paréntesis. Dentro de los
paréntesis, deberías escribir los parámetros (si el método acepta alguno), como este:

AddNumbers(3, 39);

Ya que los métodos son definidos en una clase, quizá quiera llamar un método en otra clase que
en la que se encuentra. Si es así, usted debe prefijar la llamada al método con el nombre del
objeto, o en caso de un método estático (más de ésto adelante), el nombre de la clase. Aquí hay
un ejemplo donde llamamos al método AddNumber(), el cual ha sido ubicado en otra clase
llamada MathHelper:

public void DoMath()


{
MathHelper mathHelper = new MathHelper();
int result = mathHelper.AddNumbers(4, 38);
Console.WriteLine(result);
}
Tipos de retorno de Método

Hablemos de los tipos (de datos) de retorno. En los ejemplos previos, definimos un método con un
entero como el tipo de retorno, pero usted es libre de retornar cualquier otra clase de tipo de
datos de C#. De hecho, puede incluso declarar un método el cual no retorne nada, como lo vimos
con nuestro método DoMath() arriba. Note que he sustituido int con la palabra clave void, lo cual
significa que este método se supone no regresa nada. En alunos lenguajes de programación, las
funciones sin tipo de retorno son referidas como procedures [procedimientos], pero en C#, estas
son siempre llamadas métodos.

Debe estar consciente de que cuando declara un tipo de retorno para un método, tiene que
retornar algo - de otro modo, el compilador se quejará inmediatemente:

public int AddNumbers(int number1, int number2)


{
Console.WriteLine(number1 + number2);
}
Compiler error: AddNumbers(int, int)': not all code paths return a value

Esto significa que usted necesita una (o varias) palabras reservadas return dentro de su método si
este tiene un tipo de retorno declarado. Usted puede necesitar más de un return para situaciones
donde tenga múltiples trayectorias posibles de código, como esto:

public int AddLargeNumbers(int number1, int number2)


{
if((number1 > 1000) && (number2 > 1000))
{
return number1 + number2;
}
return 0;
}

En este caso, necesitamos la segunda declaración de retorno también - si es omitida, el


compilador se quejará, porque nuestra declaración condicional puede prevenir la primera
declaración de retorno de ser alcanzada.

Resumen

Los métodos le permiten encapsular y reusar funcionalidad desde diversos lugares. Al proveer
diferentes parámetros a un método, usted puede obtener diferentes resultados. Hemos usado
algunos parámetros en los ejemplos anteriores, pero en el próximo artículo, cavaremos mucho
más profundo en el tema de los parámetros de métodos.

Clases:
Métodos con parámetros

En el artículo anterior, hablamos de métodos y tuvimos una breve introducción al concepto de


parámetros de métodos/funciones. En este artículo, profundizaremos en este tema en todas sus
variaciones. Como ya se ha mencionado, los métodos pueden funcionar sin parámetros, pero
normalmente tendrán uno o más parámetros, que ayudarán al método a cumplir su tarea.
Ya vimos un caso de uso muy simple para los parámetros en el artículo anterior: Nuestro método
AddNumbers(), que toma dos números como parámetros y devuelve la suma de estos dos
números:

public int AddNumbers(int number1, int number2)


{
return number1 + number2;
}

Esto demuestra en gran medida lo ingenioso que es el concepto de los métodos, porque con los
métodos se puede encapsular la funcionalidad en el método, pero se puede modificar el resultado
cuando se llama a este método a través de los parámetros:

AddNumbers(2, 3);

Result: 5

AddNumbers(38, 4);

Result: 42

Este es el tipo básico de parámetros, pero hablemos más sobre los diversos modificadores y
opciones que se pueden usar para cambiar el comportamiento de los parámetros.

Tenga en cuenta que en este artículo, vamos a profundizar en todos los tipos de parámetros y en
cómo pueden ayudarle, pero si acaba de empezar con C# y sólo quiere ver algunos resultados, lo
siguiente podría ser un poco demasiado complejo y técnico por ahora. Siéntase libre de saltarse el
resto del artículo y volver más tarde cuando esté listo.

Parámetros opcionales

Por defecto, cuando se llama a un método con uno o varios parámetros, estás obligado a
suministrar valores para todos estos parámetros. Sin embargo, en algunas situaciones, es posible
que tengas que hacer que uno o varios parámetros sean opcionales. En algunos lenguajes de
programación, se le permitiría hacerlo simplemente marcando el parámetro como opcional, pero
en C#, un parámetro se hace opcional suministrando un valor por defecto para él en la declaración
del método. En realidad, ésta es una buena solución, porque te ahorra tener que escribir código
adicional para hacer frente a situaciones en las que el parámetro no es suministrado por quien
llama al método.

A continuación, un ejemplo de un método con un parámetro opcional:

public int AddNumbers(int number1, int number2, int number3 = 0)


{
return number1 + number2 + number3;
}

El último parámetro (number3) es ahora opcional, porque hemos proporcionado un valor por
defecto para él (0). Al llamarlo, ahora puede proporcionar dos o tres valores, de esta forma:

public void Main(string[] args)


{
AddNumbers(38, 4);
AddNumbers(36, 4, 2);
}

Puede hacer que más de un parámetro sea opcional - de hecho, su método puede consistir sólo
en parámetros opcionales si es necesario. Sólo recuerde que los parámetros opcionales deben
ser los últimos en la declaración del método - no los primeros o entre parámetros no
opcionales.

El modificador params

Como alternativa a una serie de parámetros opcionales, se puede utilizar el modificador de params
para permitir un número arbitrario de parámetros. Podría ser parecido a esto:

public void GreetPersons(params string[] names) { }

La llamada entonces podría quedar así:

GreetPersons("John", "Jane", "Tarzan");

Otra ventaja de usar el enfoque con params, es que también se le permite pasar cero parámetros
al método. Los métodos con params también pueden aceptar parámetros normales, siempre y
cuando el parámetro con el modificador params sea el último. Por otra parte, sólo se puede utilizar
un parámetro con la palabra clave params por método. Éste es un ejemplo completo en el que
usamos el modificador params para imprimir un número variable de nombres con nuestro
método GreetPersons():

public void Main(string[] args)


{
GreetPersons("John", "Jane", "Tarzan");
}

public void GreetPersons(params string[] names)


{
foreach(string name in names)
Console.WriteLine("Hello " + name);
}

Tipos de parámetros: por valor o por referencia

C#, al igual que otros lenguajes de programación, diferencia entre dos tipos de parámetros: "por
valor" y "por referencia". El valor por defecto en C# es "por valor", lo que básicamente significa
que cuando se pasa una variable en una llamada a un método, en realidad se está enviando una
copia del objeto, en lugar de una referencia a él. Esto también significa que puedes hacer cambios
en el parámetro desde dentro del método, sin afectar al objeto original que pasaste como
parámetro.

Podemos demostrar fácilmente este comportamiento con un ejemplo:

public void Main(string[] args)


{
int number = 20;
AddFive(number);
Console.WriteLine(number);
}
public void AddFive(int number)
{
number = number + 5;
}

Tenemos un método muy básico llamado AddFive() el cual adicionará 5 al número que le pasemos.
Así en nuestro método Main(), creamos un variable llamada number con el valor de 20 y entonces
llamamos al método AddFive(). Ahora en la siguiente línea, cuando escribamos la variable number,
uno esperaría que el valor es ahora 25, pero en vez de eso, permanece como 20. ¿Porqué? Por que
por defecto, el parámetro fue pasado como una copia de objeto original (por valor), así cuando
nuestro método AddFive() trabajó en el parámetro, estaba trabajando en una copia y así nunca
afecto la variable original.

Actualmente hay dos formas de cambiar este comportamiento , así a nuestro método AddFive() le
es permitido modificar el valor original: Podemos usar el modificador ref or los modificadores
in/out

El modificador ref

El modificador ref es la abreviación de "reference" [referencia] y básacamente sólo cambia el


comportamiento del parámetro de un comportamiento por defecto de "por valor" a "por
referencia", lo que significa que ahora estamos pasando una referencia a la variable original en vez
de una copia de su valor. Aquí un ejemplo modificado:

public void Main(string[] args)


{
int number = 20;
AddFive(ref number);
Console.WriteLine(number);
}

public void AddFive(ref int number)


{
number = number + 5;
}

Notará que he agregado la palabra clave "ref" en dos lugares: En la declaración del método y
cuando paso el parámetro en la llamada al método. Con este cambio, ahora obtenemos el
comportamiento que esperabamos originalmente - el resultado es ahora 25 en vez de 20, por que
el modificador ref permite al método trabajar en el valor pasado realmente del parámetro y no en
una copia.

El modificador out

Al igual que el modificador ref, el modificador out asegura que el parámetro es pasado como
referencia en vez de como valor, peroi hay una diferencia mayor: Cuando usa el modificador "ref",
usted pasa un valor inicializado el cual puede decidir modificar dentro del método, o dejarlo como
está. Por otro lado, cuando usa el modificador out, usted forza a inicializar el parámetro dentro
del método. Esto significa que puede pasar un valor no inicializado cuando usa el modificador
"out" - el compilador se quejará si trata de terminar un método con un parámetro out sin asignarle
un valor a él.
En C#, un método puede retornar solamente un valor, pero si usa el modificador out, es capaz de
rodear esto pasando varios parámetros con el modificador out - cuando el método ha sido
llamado, los parámetros out habrán sido asignados. Aquí está un ejemplo, donde pasamos dos
números y luego, usando modificadores out, retornamos ambos una suma y una resta usando
estos números.

public void Main(string[] args)


{
int addedValue, subtractedValue;
DoMath(10, 5, out addedValue, out subtractedValue);
Console.WriteLine(addedValue);
Console.WriteLine(subtractedValue);
}

public void DoMath(int number1, int number2, out int addedValue, out int subtractedValue)
{
addedValue = number1 + number2;
subtractedValue = number1 - number2;
}
Output:

15
5

Como puede var en el inicio del ejemplo, declaro las dos variables (addedValue y subtractedValue)
antes de pasarlas como parámetros out. Esto era un requerimiento en versiones previas del
lenguaje C#, pero desde la version 6 de C#, puede declararlas directamente en la llamada al
método, así:

DoMath(10, 5, out int addedValue, out int subtractedValue);


Console.WriteLine(addedValue);
Console.WriteLine(subtractedValue);
El modificador in

Así como el modificador out, el modificador in asegura que el parámetro sea pasado como una
referencia en vez de como una copia del valor, pero a diferencia del modificador out, el
modificador in le prevendrá de hacer cualquier cambio a la variable dentro del método.

Su primer pensamiento puede ser: Si no puedo cambiar el valor del parámetro, entonces quizá
también puedo pasarlo como un parámetro regular, donde los cambios no afectarán a la variable
original. Tiene razón, el resultado aparecerá exactamente igual, pero aún habrá una razón muy
válida para usar el modificador "in": Al pasarlo como una referencia en vez de un valor, está
ahorrando recursos por que el ambiente no tiene que invertir tiempo creando una copia del objeto
cuando lo pasa al método como un parámetro regular.

En la mayoria de los casos, esto no hará mucha diferencia, por que la mayoría de los parámetros
con cadenas simples o enteros, pero en situaciones donde llame repetidamente el mismo método
muchas veces en un ciclo o donde use parámetros con valores largos, por ejemplo una cadena
muy larga o estructuras, esto puede darle una buena mejora del desempeño.

Aquí está un ejemplo sonde usamos el modificador in:

public void Main(string[] args)


{
string aVeryLargeString = "Lots of text...";
InMethod(aVeryLargeString);
}

public void InMethod(in string largeString)


{
Console.WriteLine(largeString);
}

En nuestro método, InMethod(), el parámetro largeString es ahora una referencia de solo-lectura a


la variable original (aVeryLargeString), así podemos usarla, pero no modificarla. Si lo intentamos, el
compilador se quejará:

public void InMethod(in string largeString)


{
largeString = "We can't do this...";
}

Error: Cannot assign to variable 'in string' because it is a readonly variable


Parámetros nombrados

Como lo ha visto en todos los ejemplos de arriba, cada parámetro tiene un identificador único en
la declaración del método. Esto le permite referenciar al parámetro dentro del método. Sin
embargo, cuando llama al método, usted no usa estos nombres - usted solamente provee los
valores en el mismo orden en el que fueron declarados. Esto no es un problema para métodos
simples que toman 2 o 3 parámetros, pero algunos métodos son más complejos y requieren más
parámetros. En esas situaciones, puede ser difícil darse cuenta de a que se refieren los varios
valores en la llamada al método, como en este ejemplo:

PrintUserDetails(1, "John", 42, null);

No es muy claro que significan los varios parámetros en esta llamada al método, pero si mira en la
declaración del método, puede ver esto:

public void PrintUserDetails(int userId, string name, int age = -1, List<string> addressLines =
null)
{
// Print details...
}

Pero es molesto si tiene que buscar constantemente la declaración del método para entender lo
que hacen los parámetros, así para métodos más complejos, puede suministrar los nombres de los
parámetros directamente en la llamada del método. Esto le permite suministrar los nombres de los
parámetros en cualquier orden, en vez de ser forzado a usar el orden de la declaración. Aquí está
un ejemplo:

PrintUserDetails(name: "John Doe", userId: 1);

Como un bono extra, esto le permite suministrar un valor para cualquiera de sus parámetros
opcionales, sin tener que proveer valores para todos los parámetros opcionales previos en la
declaración. En otras palabras, si quiere proveer un valor para el parámetro addressLines en este
ejemplo también tendría que proveer un valor para el parámetro age, por que viene primero en la
declaración del método. Sin embargo, si usa parámetros nombrados, el orden ya deja de importar
y puede suministrar solamente los valores para los parámetros requeridos así como uno o varios
de los parámetros opcionales, por ejemplo:
PrintUserDetails(addressLines: new List<string>() { }, name: "Jane Doe", userId: 2);

Aquí está el ejemplo completo usando parámetros nombrados:

public void Main(string[] args)


{
PrintUserDetails(1, "John", 42, null);
PrintUserDetails(name: "John Doe", userId: 1);
PrintUserDetails(addressLines: new List<string>() { }, name: "Jane Doe", userId: 2);
}

public void PrintUserDetails(int userId, string name, int age = -1, List<string> addressLines =
null)
{
// Print details...
Console.WriteLine(name + " is " + age + " years old...");
}
Resumen

A como pudo ver en este articulo, los parámetros vienen en diferentes formas y tipos.
Afortunadamente, obtendrá el camino con parámetros regulares antiguos y sencillos, pero una vez
que comience a profundizar en el lenguaje C#, se beneficiará de conocer todos los tipos y
modificadores como se explica en este artículo.

Clases:
Constructores y destructores

Los constructores son métodos especiales, usados cuando instanciamos una clase. Un constructor
nunca puede devolver nada, por lo que no tiene que definir un tipo de retorno para el. Un método
normal se define así:

public string Describe()

Un constructor es definido así:

public Car()

En nuestro ejemplo para este capítulo, tenemos una clase Car, con un constructor que toma una
cadena como argumento. Por supuesto, un constructor también puede estar sobrecargado, lo que
significa que podemos tener varios constructores, con el mismo nombre, pero con diferentes
parámetros. Aquí hay un ejemplo:

public Car()
{

public Car(string color)


{
this.color = color;
}

Un constructor puede llamar a otro constructor, lo cual puede ser útil en varias situaciones. Aquí
hay un ejemplo:
public Car()
{
Console.WriteLine("Constructor with no parameters called!");
}

public Car(string color) : this()


{
this.color = color;
Console.WriteLine("Constructor with color parameter called!");
}

Si ejecuta este código, verá que el constructor sin parámetros es llamado primero. Esto puede ser
usado para crear una instancia de varios objetos para la clase en el constructor predeterminado,
que se puede llamar desde otros constructores de la clase. Si el constructor al que desea llamar
toma parámetros, puede hacerlo también. Aquí hay un ejemplo sencillo:

public Car(string color) : this()


{
this.color = color;
Console.WriteLine("Constructor with color parameter called!");
}

public Car(string param1, string param2) : this(param1)


{

Si llamas al constructor que toma 2 parámetros, el primer parámetro se usará para invocar al
constructor que toma un parámetro.

Destructores

Desde que C# es recolectador de basura, significa que el framework liberará los objetos que ya no
use, habrán ocasiones donde necesite hacer alguna limpieza manual. Un destructor, un método
invocado una vez que el objeto es puesto a disposición, puede ser usado para limpiar recursos
usados por el objeto. Los destructores no se ven tanto como otros métodos en C#. Aquí hay un
ejemplo de un destructor de nuestra clase Car:

~Car()
{
Console.WriteLine("Out..");
}

Una vez que el objeto es recogido por el recolector de basura, este método es invocado.

Clases:
Sobrecarga de Métodos

Muchos de los lenguajes de programación soportan una técnica llamada parámetros por defecto u
opcionales. Permite al programador hacer uno o mas parámetros opcionales, dándoles un valor
por defecto. Esto es especialmente práctico cuando se añade funcionalidad al código existente.

Por ejemplo, tal vez desees añadir funcionalidad a una función ya existente que ahora requiere que
se agreguen uno o más parámetros. Al agregarlos, podrías hacer que el código que llama a esta
función no funcione más puesto que no se estarían pasando los nuevos parámetros. La solución a
este problema sería que definieras los parámetros añadidos como opcionales y darles un valor por
default que hagan que la función asuma los valores en caso de que no se le pasaran los
parámetros requeridos.

Los parámetros por defecto fueron introducidos en la versión 4.0 de C#, pero hasta ese punto,
quienes escribían código en C# habían estado utilizando otra técnica que, básiciamente, hace lo
mismo llamada sobrecarga de métdoos. Esto permite al programador definir varios métodos con
el mismo nombre, siempre y cuando reciban un conjunto distinto de parámetros. Cauando utilizas
clases de .NET, puedes notar rápidamente que la sobrecarga de métodos está en todos lados. Un
buen ejemplo de esto es el método Substring de la clase String; está sobrecargado y se ve así:

string Substring (int startIndex)


string Substring (int startIndex, int length)

Pues llamarla ya sea con uno o dos parámetros. Si solo la llamas con un parámetro, el parámetro
de longitud se llena con la longitud total de la cadena, ahorrándonos tiempo cuando simplemente
queremos obtenenr la última parte de la cadena.

Así que, al definir varias versiones de la misma función, ¿cómo evitamos tener el mismo código en
varios lugares? Fácil: dejamos que la versión simple del método haga que la versión complicada le
haga todo el trabajo. Mira el siguiente ejemplo:

class SillyMath
{
public static int Plus(int number1, int number2)
{
return Plus(number1, number2, 0);
}

public static int Plus(int number1, int number2, int number3)


{
return number1 + number2 + number3;
}
}

Definimos al método Plus en dos versiones. La primera toma dos parámetros para sumarlos,
mientras que la segunda toma tres parámetros. La suma se realiza en la versión que requiere tres
parámetros, si solo queremos sumar dos, llamamos a la versión de tres parámetros y le pasamos
un cero, actuando como valor por defecto. Lo sé, lo sé, como lo indica el nombre de la clase, es un
ejemplo muy simple; pero debería darte una idea de cómo funciona esto de la sobrecarga.

Ahora, cuando te sientas con ganas de hacer matemáticas avanzadas y sumar cuatro números
(broma), es muy simple agregar una nueva sobrecarga al método:

class SillyMath
{
public static int Plus(int number1, int number2)
{
return Plus(number1, number2, 0);
}

public static int Plus(int number1, int number2, int number3)


{
return Plus(number1, number2, number3, 0);
}
public static int Plus(int number1, int number2, int number3, int number4)
{
return number1 + number2 + number3 + number4;
}
}

Lo genial de esto es que todas tus llamadas al método Plus existentes seguirán funcionando como
si nada hubiera cambiado. Entre más C# utilices, más apreciarás la sobrecarga de métodos.

Clases:
Herencia

La herencia es uno de los conceptos más importantes de la Programación Orientada a Objetos


(POO) y uno de los fundamentos sobre los cuales está construido C#. Herencia se refiere a la
capacidad de crear clases que heredan ciertos aspectos de otras clases primarias. Todo el
framework de .NET se basa en el concepto de Herencia y por esto en .NET "todo es un objeto".
Incluso un simple número es una instancia de una clase que se hereda de la clase System.Object,
aunque .NET haga un poco de trampa acá para facilitar la vida de los usuarios y por esto se puede
asignar un número directamente, sin tener que crear una nueva instancia de cada número que se
vaya a usar.

La Herencia puede ser un aspecto difícil de comprender, así que comencemos con unos ejemplos
simples:

public class Animal


{
public void Greet()
{
Console.WriteLine("Hello, I'm some sort of animal!");
}
}

public class Dog : Animal


{

Primero, definimos una clase llamada Animal, con un método llamado Greet el cual imprime un
saludo en la consola. Luego definimos una clase de perro, y con el operador dos puntos ':' le
decimos a C # que la clase de perro hereda de la clase de animal. Lo elegante de este método es
que tiene sentido en el mundo real: un perro ES un animal. Intentemos usar las clases:

Animal animal = new Animal();


animal.Greet();
Dog dog = new Dog();
dog.Greet();

Si corres este ejemplo, notarás que aunque no hayamos definido un método Greet() para la clase
Dog, todavía sabe cómo saludarnos, ya que hereda este método de la clase Animal. Sin embargo,
este saludo es un poco genérico, por lo que vamos a personalizarlo cuando definamos qué animal
es:

public class Animal


{
public virtual void Greet()
{
Console.WriteLine("Hello, I'm some sort of animal!");
}
}

public class Dog : Animal


{
public override void Greet()
{
Console.WriteLine("Hello, I'm a dog!");
}
}

Aparte del método Greet() en la clase Dog, debes notar dos cosas: en la clase Animal he agregado
la palabra reservada "virtual" y en la clase Dog uso la palabra reservada "override"

En C#, no esta permitido sobreescribir un miembro de una clase a menos que este marcado como
virtual. Sí quieres, puedes aun así acceder al método heredado, incluso después de sobre
escribirlo, usando la palabra clave "base".

public override void Greet()


{
base.Greet();
Console.WriteLine("Yes I am - a dog!");
}

Sin embargo los métodos no son lo único que se hereda. De hecho, casi todos los miembros de la
clase serán heredados, incluidos los campos y las propiedades. Sólo recuerda las reglas de
visibilidad, como se discutió en un capítulo anterior.

Se puede tener toda una jerarquía de clases que hereden una de la otra. Por ejemplo, podríamos
crear una clase Puppy que hereda de nuestra clase Dog que a su vez hereda de la clase Animal. Lo
que no puede hacer en C # es que una clase herede de más de una clase al mismo tiempo. La
herencia múltiple, como es conocida, no es compatible con C#.

Collections / Colecciones:
Lists / Listas

C# tiene un amplio rango de clases para lidear con listas. Implementando la interfaz de iList y la
implementación mas popular es la lista generica, normalmente referida como List<T> La "T"
especifica el tipo de objeto contenido en la lista, el cual tiene el beneficio añadido de que el
compliador verificará y se asegurará que se agreguen unicamente objetos del tipo de la lista - en
otras palabras la lista tipo -List <Type> es segura para agregar elementos de un mismo tipo.

List es muy parecida a la clase ArrayList, que era la opción de ir a la lista antes de que C #
admitiera listas genéricas. Por lo tanto, también verá que List puede hacer muchas de las mismas
cosas que una Matriz (que por cierto también implementa la interfaz IList), pero en muchas
situaciones, List es más simple y fácil de trabajar. Por ejemplo, no tiene que crear una Lista con un
tamaño específico; en su lugar, puede crearla y .NET la expandirá automáticamente para ajustarse
a la cantidad de elementos a medida que los agregue.
Como mencioné, la T se refiere a tipo y se usa para especificar el tipo de objetos que quieres que
contenga la lista. En nuestro primer ejemplo, te mostraré como crear una lista que debería
contener strings:

List<string> listOfStrings = new List<string>();

Esto crea una lista vacía, pero agregarle algo luego es muy fácil con el método Add:

listOfStrings.Add("a string");

Sin embargo, si intenta agregar algo que no sea una cadena, el compilador se quejará de
inmediato:

listOfStrings.Add(2);
Error CS1503 Argument 1: cannot convert from 'int' to 'string'
Inicializando una lista con ítems

En el ejemplo anterior, acabamos de crear una lista y luego le agregamos un elemento. Sin
embargo, C # realmente le permite crear una lista Y agregarle elementos dentro de la misma
declaración, utilizando una técnica llamada inicializadores de colección. Veamos cómo se hace:

List<string> listOfNames = new List<string>()


{
"John Doe",
"Jane Doe",
"Joe Doe"
};

La sintaxis es bastante simple: antes del punto y coma final habitual, tenemos un conjunto de
llaves, que a su vez contiene una lista de los valores que queremos que estén presentes en la lista
desde el principio. Como se trata de una lista de strings, los objetos iniciales que proporcionamos
deben ser del tipo de string. Sin embargo, se puede lograr exactamente lo mismo para la lista de
otros tipos, incluso si estamos usando nuestras propias clases, como demostraré en el siguiente
ejemplo.

Trabajando con los elementos.

Hay varias formas de trabajar con los elementos de una lista genérica y mostrar algunos de ellos.
He creado un ejemplo más amplio:

using System;
using System.Collections.Generic;

namespace Lists
{
class Program
{
static void Main(string[] args)
{
List<User> listOfUsers = new List<User>()
{
new User() { Name = "John Doe", Age = 42 },
new User() { Name = "Jane Doe", Age = 34 },
new User() { Name = "Joe Doe", Age = 8 },
};
for(int i = 0; i < listOfUsers.Count; i++)
{
Console.WriteLine(listOfUsers[i].Name + " is " + listOfUsers[i].Age + " years old");
}
Console.ReadKey();
}
}

class User
{
public string Name { get; set; }

public int Age { get; set; }


}
}

Comencemos desde abajo, donde definimos una clase simple para almacenar información sobre
un Usuario, solo un nombre y una edad. Volvemos a la parte superior del ejemplo, donde he
cambiado nuestra lista para usar esta clase de usuario en lugar de cadenas simples. Utilizo un
inicializador de colección para completar la lista con los usuarios: observe cómo la sintaxis es la
misma que antes, solo que es un poco más compleja porque se trata de un objeto más complejo
que una cadena.

Una vez que tenemos lista la lista, utilizo un bucle for para ejecutarla - para saber cuántas
iteraciones vamos a hacer, uso la propiedad Count de la lista. En cada iteración, accedo al usuario
en cuestión a través del indexador de la lista, utilizando la sintaxis de corchetes (por ejemplo,
listOfUsers [i]). Una vez que tengo el usuario, escribo el nombre y la edad.

Añadiendo, insertando y borrando elementos.

Ya intentamos agregar un solo elemento a una lista, pero hay más opciones para hacerlo. En
primer lugar, puede insertar un elemento en lugar de agregarlo; la diferencia es que, si bien el
método Addsiempre agrega al final de la lista, el método Insertle permite insertar un elemento en
una posición específica. Aquí hay un ejemplo:

List<string> listOfNames = new List<string>()


{
"Joe Doe"
};
// Insert at the top (index 0)
listOfNames.Insert(0, "John Doe");
// Insert in the middle (index 1)
listOfNames.Insert(1, "Jane Doe");

Comenzamos la lista con solo un elemento, pero luego insertamos dos elementos más, primero en
la parte superior de la lista y luego en el medio. El primer parámetro del método Insert es el índice
donde queremos insertar el elemento. Sin embargo, tenga cuidado: se lanzará una excepción si
intenta insertar un elemento en el índice 3, ¡si la lista tiene menos elementos!

Añadiendo múltiples elementos.

Al igual que tenemos los métodos Agregar e Insertar para agregar un solo elemento, también hay
métodos correspondientes para agregar e insertar múltiples elementos. Se llaman AddRange()()
e InsertRange() () y acepta cualquier tipo de colección que implemente la
interfaz IEnumerable como parámetro; esto podría ser, p.e. una matriz de elementos u otra lista,
qué elementos desea agregar o insertar en la lista actual.

Como ejemplo de los métodos Range, hagamos algo divertido: combinamos el método AddRange
con un inicializador de colección para agregar varios nombres nuevos a una lista existente en una
sola declaración:

listOfNames.AddRange(new string[]
{
"Jenna Doe",
"Another Doe"
});

Simplemente creamos una serie de cadenas sobre la marcha e inmediatamente agregamos sus
elementos a nuestra lista de nombres del ejemplo anterior.

Borrando elementos.

Actualmente hay tres métodos a su disposición cuando desea eliminar uno o varios elementos de
una lista: Remove(), RemoveAt() y RemoveAll().

El método Remove() toma solo un parámetro: el elemento que desea eliminar. Esto es genial para,
p.e. una lista de cadenas o enteros, porque simplemente puede escribir el elemento que desea
eliminar. Por otro lado, si tienes una lista de objetos complejos, primero deberías encontrar ese
objeto, para tener una referencia que puedas pasar al método Remove (). "Vamos a tratar eso más
tarde. Aquí hay un ejemplo muy básico sobre cómo puedes eliminar un solo elemento con el
método Remove ():

List<string> listOfNames = new List<string>()


{
"John Doe",
"Jane Doe",
"Joe Doe",
"Another Doe"
};

listOfNames.Remove("Joe Doe");

El método Remove () simplemente recorre la lista en iteración hasta que encuentra la primera
instancia del objeto que especificó para su eliminación, y ellos la eliminan; solo elimina una
instancia, y si especifica un elemento en la lista que no existe, No se arroja ningún error. El método
devuelve true si pudo eliminar un elemento y false si no lo fue.

El método RemoveAt() aprovecha el hecho de que la lista genérica se basa en índices al permitirle
eliminar un elemento en función de su índice / posición en la lista. Por ejemplo, podría eliminar el
primer elemento de la lista así:

listOfNames.RemoveAll(0);

O el último elemento de la lista así:

listOfNames. RemoveAll (listOfNames.Count - 1);


Nuevamente, esto solo elimina un solo elemento y esta vez, debe tener cuidado al proporcionar el
índice del elemento que se eliminará; si utiliza un índice que está fuera de los límites (inferior a 0 o
superior a la cantidad de elementos) se lanzará una excepción! Entonces, a menos que esté seguro
de lo que está haciendo, es posible que desee envolver el método RemoveAll () en un bloque try-
catch para manejar la excepción (explicado en detalle en otra parte de este tutorial). El método
RemoveAll () no devuelve nada, por lo que tendrá que verificar la cantidad de elementos en la lista
antes y después de la llamada, para decidir si fue exitosa, por otro lado, si sabe que tiene un índice
que existe en la lista, de la que siempre debe asegurarse, siempre puede esperar que RemoveAll ()
sea exitoso.

RemoveAll() es el más complejo de los métodos remove, pero definitivamente también el más
poderoso. Toma un delegado a un método como parámetro y este método decide si un elemento
debe eliminarse o no devolviendo verdadero o falso. Esto le permite aplicar su propia lógica al
eliminar elementos y también le permite eliminar más de un elemento a la vez. Los delegados
serán tratados en otra parte de este tutorial, porque es un tema grande y complejo, pero todavía
quiero que entiendan lo genial que es el método RemoveAll, así que aquí hay un ejemplo:

List<string> listOfNames = new List<string>()


{
"John Doe",
"Jane Doe",
"Joe Doe",
"Another Doe"
};

listOfNames.RemoveAll(name =>
{
if (name.StartsWith("J"))
return true;
else
return false;
});

En este ejemplo, utilizamos un método anónimo (nuevamente demasiado complejo para ser
explicado aquí, pero será tratado en otro capítulo) como un parámetro para el método RemoveAll.
Nuestro método anónimo es bastante simple: se llamará para cada elemento de la lista y tendrá
un parámetro llamado name, que es, por supuesto, el elemento actual en la iteración. Mira este
nombre y si comienza con "J", se devuelve true; de lo contrario, es false. El método RemoveAll ()
usa esta respuesta (verdadero o falso) para decidir si cada elemento debe eliminarse o no. Al final,
esto deja nuestra lista inicial con solo un miembro de Doe: Another Doe.

Ordenando elementos de Lista

Hasta ahora, los elementos de la lista con los que hemos trabajado se han utilizado en el orden en
que se agregaron a la lista. Sin embargo, es posible que desee ordenar los elementos de una
manera específica, p. alfabéticamente en el caso de nuestra lista de nombres. La List<T> tiene un
método Sort () que podemos usar para esto:

List<string> listOfNames = new List<string>()


{
"John Doe",
"Jane Doe",
"Joe Doe",
"Another Doe"
};
listOfNames.Sort();
foreach (string name in listOfNames)
Console.WriteLine(name);

Como verá en la salida, los elementos de la lista ahora se han ordenado alfabéticamente, y si lo
desea en orden descendente (de Z a A), simplemente llame al método Reverse()después de
realizar la clasificación:

listOfNames.Sort();
listOfNames.Reverse();

Entonces ordenar una lista fue bastante fácil, ¿verdad? Bueno, fue muy fácil porque tenemos una
lista de cadenas y .NET Framework sabe exactamente cómo comparar dos cadenas. Si tiene una
lista de números, .NET, por supuesto, también sabrá cómo ordenar eso. Por otro lado, es posible
que tenga una lista de objetos personalizados (ya que laList<T>puede contener cualquier objeto)
que .NET no tiene la posibilidad de saber cómo comparar. Hay varias soluciones a este problema,
p. implementando la interfaz IComparable o usando LINQ (lo veremos más adelante en este
tutorial), pero como solución rápida, también podemos proporcionar un método para que llame el
método Sort (), para aprender cómo se comparan dos elementos uno contra el otro, así:

using System;
using System.Collections.Generic;

namespace ListSort
{
class Program
{
static void Main(string[] args)
{
List<User> listOfUsers = new List<User>()
{
new User() { Name = "John Doe", Age = 42 },
new User() { Name = "Jane Doe", Age = 39 },
new User() { Name = "Joe Doe", Age = 13 },
};
listOfUsers.Sort(CompareUsers);
foreach (User user in listOfUsers)
Console.WriteLine(user.Name + ": " + user.Age + " years old");
}

public static int CompareUsers(User user1, User user2)


{
return user1.Age.CompareTo(user2.Age);
}
}

public class User


{
public string Name { get; set; }
public int Age { get; set; }
}
}

Esto agregó bastante código a nuestro ejemplo, pero en realidad no es demasiado complicado. Si
comenzamos desde abajo, he creado una clase de usuario muy simple, que consta de un nombre y
una edad. En el medio, he declarado un método llamado CompareUsers(): toma dos usuarios como
parámetros y luego devuelve un número entero, que indicará si un elemento es "más pequeño",
"igual" o "más grande" (-1, 0 o 1). El método Sort () utilizará estos valores para mover los
elementos de modo que el orden de los elementos coincida con lo que queremos. En este caso,
simplemente uso la propiedad Age para comparar, esencialmente dejándonos con una lista de
usuarios ordenados por su edad.

Resumen

Este artículo es uno de los más largos en este tutorial, pero espero que haya aprendido mucho
sobre las listas, porque mientras más programación haga, más se dará cuenta de lo importantes
que son las listas y los diccionarios. Hablando de diccionarios, los discutiremos en el próximo
artículo.

C# 3.0:
Translate
Collection Initializers

Así como C# 3.0 ofrece una nueva manera de inicializar objetos, una nueva sintáxis para inicializar
una lista con un conjunto específico de elementos, ha sido incluida. Podemos usar la clase Car del
capítulo anterior:

class Car
{
public string Name { get; set; }
public Color Color { get; set; }
}

Si quisieramos crear una lista para contener un rango de vehículos, tendríamos que hacer algo
como esto en C# 2.0:

Car car;
List<Car> cars = new List<Car>();

car = new Car();


car.Name = "Corvette";
car.Color = Color.Yellow;
cars.Add(car);

car = new Car();


car.Name = "Golf";
car.Color = Color.Blue;
cars.Add(car);

Usando inicializadores de objetos, podríamos hacer un poco más corto:

List<Car> cars = new List<Car>();


cars.Add(new Car { Name = "Corvette", Color = Color.Yellow });
cars.Add(new Car { Name = "Golf", Color = Color.Blue});

Sin embargo, puede ser aún más simple, cuando lo combinamos con inicializadores de
colecciones:

List<Car> cars = new List<Car>


{
new Car { Name = "Corvette", Color = Color.Yellow },
new Car { Name = "Golf", Color = Color.Blue}
};

O en la versión de una línea, la cual hace exactamente lo mismo:

List<Car> cars = new List<Car> { new Car { Name = "Corvette", Color = Color.Yellow }, new
Car { Name = "Golf", Color = Color.Blue} };

10 líneas de código han sido reducidas a una, aunque una línea un poco larga, gracias a los
inicializadores de objetos y colecciones.

También podría gustarte