Curso de Programación en C#

[Comenzando con C#]

(Hoy es dia de ver objetos con C# y comenzar lo bueno :-D)

Fecha: 30/Abr/2005 (22 de Abril de 2005)
Autor: Alejandro Domingo Velazquez Cruz [email protected]

 

 Por favor no se olviden de calificar este curso, su opinión cuenta, buena o mala será tomada en cuenta

Clases


Lo primero que nos viene a la mente cuando leemos esto es ¿Qué es una clase? e inmediatamente después de eso nos preguntamos
¿Para qué sirve una clase?, bueno una definición de clase sería que es una representación abstracta de un objeto, pero esto
nos deja con bastantes interrogantes, veamoslo en español, je, je, je, un objeto puede ser cualquier cosa, por ejemplo una
casa, la clase es la reprentación abstracta del objeto, en este caso serian los planos de la casa, en ellos la casa se ve 
representada por completo, ya que en el plano podemos ver que tendrá la casa, donde lo tendrá, como estará hecho, más sin 
embargo no podemos sentarnos en las escaleras del plano de la casa porque la casa aún no existe, la clase es solo el plano del
que después podremos construir el objeto, más sin embargo una clase por si sola no nos es de utilidad en lo absoluto hasta
que la convertimos en un objeto, es decir no podemos habitar el plano de una casa, necesitamos construirla es decir construir
el objeto, a dicho proceso de construcciòn se le llama instancia. Ejemplo:

class Casa
{
	decimal Altura;
	decimal Ancho;
	decimal Profundidad;
	string  Color;
	void Entrar();
	void Salir();
}

Ahora tenemos un ejemplo mas formal de lo que sería una clase, en este ejemplo declaramos una clase llamada Casa, una Casa
en la vida real tiene diversas caracteristicas o "Atributos", la clase guarda dentro de si tanto los atributos de la Casa como
la forma en que se comportara, dicho comportamiento define como reaccionara ante otras clases. Una vez que se ha definido
la clase Casa TODA clase que heredase de ella tendria todas las caracteristicas de nuestra Casa original.

Objetos


Un objeto es la consolidación de una clase, retomando el ejemplo de la casa, deciamos que los planos de la Casa serian la
Clase casa, es decir la representación abstracta del objeto miCasa, pero para que podamos utilizar la clase Casa necesitamos
instanciarla, la instancia es el proceso de construir la Casa. Ejemplo:

class Casa
{
	decimal Altura;
	decimal Ancho;
	decimal Profundidad;
	string  Color;
	void Entrar();
	void Salir();
}

Tomando como Base la Clase Casa ahora lo que haremos sera declarar miCasa del tipo Casa. Ejemplo:

Casa miCasa = new Casa();

Quiza sea un poco dificil de captar al principio, pero en realidad es de lo mas sencillo, miren toda clase es un tipo de datos
es decir, en el mundo real existen numeros no decimales menores a cierto tamaño, bueno en C# se creo una clase llamada int
para representarlos de manera abstracta, es por esto que cada que declaramos un entero declaramos:

int  miNumero = 0;
tipo  nombre   valor

Pero porque usamos new y los (), bueno, porque en el caso de los enteros(int) simplemente contienen un dato y no hacen nada mas
son simples contenedores, sin embargo las clases que a nosotros nos interesan y que son las que estaremos creando deben actuar
de distintas maneras, ya sea leyendo bases de datos, actualizando paginas web, realizando operaciones matematicas, etc.,
es por esto que se utiliza el operador new, eso nos permite mandarle datos a la clase que hemos creado, supongamos que nuestra
clase Casa acepta 3 parametros, Nombre del dueño, Precio y Si esta a la venta o no, bueno pues en ese caso podriamos 
inicializar nuestra casa asi:

Casa miCasa = new Casa("Jorge","$1,408,506","SI");

Estos parametros de entrada provocan que el objeto "reaccione" realizando una serie de acciones, como por ejemplo, publicar 
la casa en un sitio web de venta de casas, es un ejemplo sencillo, pero que nos da una idea de como reaccionan los objetos.
Una caracteristica de los objetos es que presentan siempre 3 caracteristicas: Identidad, Comportamiento y Estado.

+ Identidad: 		Nos permite distinguir a un objeto de los demás obejtos en la misma clase, por ejemplo imaginemos que
 			tenemos una casa igual que la de nuestro vecino, como en los conjuntos habitacionales donde todas las 
			casas son iguales en ese caso el Número de casa es lo que nos distingue, porque aunque tuvieran el 
			mismo color, los mismos arreglos al frente, etc., 2 casas no pueden tener el mismo numero.

+ Comportamiento: 	Esto hace utiles a los objetos, ya que los objetos existen para hacer algo, si los objetos no hicieran 
			nada no tendría chiste crearlos, el comportamiento hace accesible a un objeto, asi mismo los objetos
			que vienen de una misma clase comparten el mismo comportamiento, por ejemplo, una Casa es una Casa
			porque se puede habitar, un Carro es un Carro porque se puede manejar, etc.

+ Estado:		Tiene que ver con el funcionamiento interno de una clase, dicho funcionamiento interno es el que le
			permite proporcionar su comportamiento. Un objeto bien diseñado mantiene inaccesible su estado, esto
			es porque a uno no le interesa como le hace un objeto para hacer lo que hace, a uno simplemente le
			interesa que lo haga.

Para diseñar objetos es importantisimo tomar en cuenta lo siguiente:

+ Decidir lo que es importante y lo que no, es decir, enfocarse en lo que es importante y depender sólo de ello.
+ Ignorar lo que no es importante y no depender de ello, basandonos en el principio de Dependencia mínima
+ Utilizar la Encapsulación para aplicar la Abstracción.

Es fácil quedar abrumado ante la inmensa cantidad de atributos y comportamientos que puede tener un objeto es por ello que
es de vital importancia concentrarse tan solo en aquello que es importante, si en los planos se incluyeran detalles de donde
pondrá la lavadora, la t.v., donde encadenará al perro, donde estará el clavo para sujetar su reloj, etc., sería increiblemente
tedioso y abrumador el representarlo, es por esto que solo se debe uno concentrar en los detalles que REALMENTE importan, a eso
se le llama abstracción. La abstracción es una técnica que nos permite quitarle a un objeto todos aquellos comportamientos que
son innecesarios hasta que el objeto solo tiene dentro de si aquellos comportamientos que son esenciales para su funcionamiento,
una buena abstracción elimina todos los detalles poco importantes y le permite enfocarse y concentrarse SOLO en los detalles
importantes. Debemos recordar que un objeto deberá siempre aplicar el concepto de Dependencia mínima por motivos de encapsulación
la dependencia mínima permite que las cosas complejas sean simples, esto se logra al ocultar los aspectos no esenciales de una
clase, estos aspectos una vez ocultados, no se pueden ver ni utilizar.

Encapsulación


+	Combinar datos y métodos en una entidad única
+	Controlar el acceso a los datos y los métodos
		- Público Accesible desde fuera
		- Privado Accesible solo desde adentro

Pero ¿Porqué es útil encapsular?, sencillo, la encapsulación minimiza el impacto, es decir si algún día nos vemos en la necesidad
de actualizar algunas clases de nuestro programa, entre menos dependamos de una clase menos impacto tendrá el hecho de que se
cambie, así mismo nos permité esconder los detalles de implementación de nuestro programa, lo que aumenta su resistencia a 
ataques, esto debido a que al declarar metodos y/o variables como privadas las ocultamos y dejan de ser visibles para las demas
clases.

Creación de una Clase Sencilla


class Persona
{
	public int Edad;
	public string Nombre;	
	private string Direccion;
}

Lo que hacemos es declarar dos variables publicas que pueden ser accedidas sin problemas desde el exterior y una variable privada
que solo puede ser utilizada por miembros de la clase, ahora para utilizar nuestra clase persona necesitamos crear una persona:

	Persona Alejandro = new Persona(); //Acabo de nacer, ¿como ven?, je, je, je
	Alejandro.Edad = 22;
	Alejandro.Nombre = "Alejandro Domingo Velazquez Cruz";

Recordemos que cada objeto se trata por separado, es decir por referencia, lo cambios que realicemos a cualquier otra persona
que creemos no afectaran a Alejandro, por ejemplo:

	Persona Carmen = new Persona();
	Carmen.Edad = 21;
	Carmen.Nombre = "María del Carmen Lopez Villegas";

Alejandro.Nombre sigue siendo Alejandro Domingo Velazquez Cruz y Carmen.Nombre es María del Carmen Lopez Villegas y los cambios
que realicemos a cualquiera de ellos no afectan al otro en lo absoluto. Ahora veamos si nosotros pusieramos algo así:

	Alejandro.Direccion = "Norte 5";

El compilador marcaría error, ya que esa variable solo puede ser accedida desde adentro de la clase, ya sea por medio de algún
método público o declarando un atributo público para permitir el acceso a ese dato, es decir, nosotros sabemos que existe la
variable porque nosotros hemos hecho la clase, pero sin embargo no se puede acceder a esa variable, ni siquiera sabiendo que
existe, a esto se le llama ocultamiento, ya que la variable queda "oculta" a las demás clases, podemos ocultar tanto variables
como métodos e inclusive se pueden ocultar clases. Por último hemos aprendido a crear objetos, pero y ¿Comó destruirlos?, bueno
C# hace uso de la recuperación automatica de memoría gracias a su Recolector de Basura, lo más que podemos hacer es marcar un
objeto para indicar que ya puede ser recolectado, esto generalmente no es necesario, ya que el Recolector de Basura detecta cuando
un objeto ya no está siendo utilizado porque no ahi ningún otro objeto que tenga su dirección, pero eso lo veremos después.

Miembros estáticos versus instancias


+ Los miembros estáticos los comparten todas las instancias de una clase
+ Los miembros de instancia pertenecen a instancias específicas de una clase

Es decir cuando declaramos una variable estática todos los objetos creados a partir de esa clase tendrán esa variable en común
esto es útil por ejemplo en los sistemas bancarios, no es practico ni comodo el hecho de que cada objeto almacene el tipo de cambio
es más practico que se declare estático y sea compartido por todos los objetos, ya que el tipo de cambio es el mismo para todos 
ellos, ejemplo:

class Cliente
{
	static decimal tipoCambio;
}

Esto quiere decir que en lugar de declarar la variable tipoCambio a nivel de objeto la estamos declarando a nivel de clase, en un
lenguaje estructurado esto lo convertiría en una variable global, con sus respectivas ventajas y desventajas, que es accesible a todos, 
pero a cambio una variable global no puede ser encapsulada, sin embargo los lenguajes orientados a objetos no permiten las variables 
globales, sin embargo tienen lo que se conoce como variables estáticas. Los datos estáticos se declaran dentro de clase beneficiandose
de la encapsulación de la clase, pero están asociados con la clase misma y no con cada objeto, es decir que aunque nunca se genere
un objeto esa variable ocupará espacio. Puede haber tanto variables como métodos estáticos y son compartidos por todas las instancias
de la clase, es decir, son comunes a todos los objetos generados a partir de dicha clase. Ahi que tomar en cuenta algo muy importante
Cuando un método se declara estático, dicho método SOLO es accesible a travéz de la clase y no a travéz de las instancias de la clase
los métodos estáticos solo pueden ser accesados por los datos estáticos y por otros métodos estáticos, además debido a que son parte
de la clase pueden ser accesados sin crear una instancia de la misma. En C# no se puede acceder a un método estático desde una instancia.

public class Cliente
{
	public string nombreCliente;
	public void consultaNombreCliente()
	{
		Console.WriteLine(nombreCliente);
	}

	public static string nombreBanco;
	public static void consultaNombreBanco()
	{
		Console.WriteLine(nombreBanco);
	}
}

Cliente miCliente = new Cliente();
miCliente.nombreCliente = "Alejandro Velazquez"; //Definó el nombre del cliente
miCliente.consultaNombreCliente(); //Esto imprimirá el nombre del cliente

Cliente.nombreBanco = "BANAMEX"; //Definó el nombre del banco
Cliente.consultaNombreBanco(); //Esto imprimirá el nombre del banco

El nombre del banco y la consulta al nombre del banco se realizan directamente a travéz de la clase, no pueden realizarse a travéz de las
instancias de la clase, esto debido a que son variables estáticas, sin embargo el nombre del cliente y la consulta al nombre del cliente
se realizan a travéz de las instancias de la clase, esto es especialmente útil debido a que en este ejemplo el nombre del banco es algo
común a todos los clientes, porque todos son clientes del mismo banco, sin embargo, cada cliente tiene su propio nombre, así que lo que
hicimos es declarar el nombre del banco y la consulta al nombre del banco estáticas con esto el dato se guarda una única vez en la clase
y solo puede ser accedida a travéz de la clase, así mismo declaramos normalmente el nombre del cliente y la consulta al nombre del cliente
ya que cada que creemos un objeto cliente será una persona diferente con un nombre diferente y por lo tanto el valor del campo cambiará en
cada instancia.

Ahora veamos, hemos visto distintas formas de encapsular y manipular los datos, pero ¿Qué pasa si necesitamos acceder a un método o dato
privado desde afuera?, bueno para eso existen las propiedades y son realmente utiles, las propiedades nos permiten manipular datos privados
desde afuera de la clase, ejemplo:

class Persona
{		
	
	public string nombreCompleto()
	{
		Console.WriteLine(Nombre+" "+Apellido);
	}

	public string nombre
	{
		get{ return Nombre; }
		set{ Nombre = value ; }
	}

	public string apellido
	{
		get{ return Apellido; }
		set{ Apellido = value; }
	}
	private string	Nombre,Apellido;
}

En este ejemplo se declaran dos variables privadas, Nombre y Apellido si intentaramos acceder a dichas variables desde el exterior de la
siguiente manera:

Persona Alejandro = new Persona(); //Ja, ja, ja he vuelto a nacer... ;-)
Alejandro.Nombre = "Alejandro";
Alejandro.Apellido = "Velazquez";

Esto nos marcaría un error ya que dichos datos han sido declarados como privados y por tanto son invisibles desde afuera de la clase, lo que
podemos hacer en cambio es accederlos a travéz de propiedades de la siguiente manera:

Alejandro.nombre = "Alejandro";
Alejandro.apellido = "Velazquez";
Alejandro.nombreCompleto();

El ejemplo se ve muy similar al anterior, pero ahí una sútil diferencia.

public string nombre	//El nombre de la propiedad se parece al de la variable, solo cambia la primera letra, esto la hace fácil de recordar
{
	get{ return Nombre; }    //get nos permite recuperar el valor de la variable privada
	set{ Nombre = value ; }  //set nos permite modificar el valor de la variable privada
}

La propiedad llamada nombre nos permite modificar el valor de la variable privada llamada Nombre, pero va mas alla de eso, gracias a las
propiedades podemos validar los datos y asegurarnos que son confiables antes de volcarlos sobre nuestras variables privadas y viceversa, 
asegurarnos que va a recibir el valor de la variable privada alguien que nosotros queremos que reciba el valor de la variable privada.
Además las propiedades nos ayudan a proporcionar encapsulación. Ejemplo:

class Persona
{		
	
	public string nombreCompleto()
	{
		Console.WriteLine(Nombre+" "+Apellido);
	}

	public int edad
	{
		get
		{
			return Edad;	
		}	
		set
		{
			if( 0 < value && value < 100) //validamos que sea una Edad valida de acuerdo a nuestro criterio
				Edad = value;	      //De ser asi modificamos el valor de la variable privada		
		}
	}

	public string nombre
	{
		get{ return Nombre; }
		set{ Nombre = value ; }
	}

	public string apellido
	{
		get{ return Apellido; }
		set{ Apellido = value; }
	}

	private int Edad;
	private string	Nombre,Apellido;
}

Utilizando el ejemplo anterior hemos agregado una variable nueva llamada Edad de tipo entero, ya que las edades no pueden ser negativas
y como estamos asumiendo que ninguno de es casi imposible que nos topermos con una persona de 100 o mas años, pues condicionamos la
propiedad para que solo acepte  valores enteros que sean mayores que 0 y menores que 100, esta es un sencillo ejemplo de como utilizar
las propiedades para validar las entradas suministradas por el usuario, esto es sumamente útil ya que nos ahorra muchos errores en tiempo
de ejecución, así mismo nos permite tener una aplicación más segura.

Alcance


El alcance delimita que regiones de código pueden referenciar un elemento del programa, los modificadores de acceso son los que definen
el alcance de los miembros de la clase y son:

	Capacidad de Acceso	Significado
	
	public			El acceso no se encuentra restringido
	protected		El acceso está limitado a la clase actual, así como a las clases que hereden de ella
	internal		El acceso está limitado al proyecto actual
	internal protected	El acceso está limitado al proyecto actual, así como a los tipos derivados de ella
	private			El acceso está limitado a la clase actual

Reglas

Los espacios de nombre siempre son públicos (implícitamente).
Las clases siempre son públicas (implícitamente).
Los miembros de clase son privadas por predeterminación.

Todo esto a menos que se agregue un modificador de alcance para variar la capacidad de acceso el elemento.

Recomendaciones

Solo deben hacerse públicos aquellos datos que los usuarios de su clase necesiten ver, aunque siempre es más recomendable en medida de lo
posible el uso de atributos para acceder a dichos valores, NO DECLARE COMO PUBLICA UNA VARIABLE A MENOS QUE SEA 
E-S-T-R-I-C-T-A-M-E-N-T-E necesario. Para los efectos del curso se tratarán las variables de una manera no completamente segura por razones
de legibilidad del código, pero una vez que obtengan más experiencia deben evitarlo.

Constructores y Destructores


Los constructores nos permiten inicializar la memoria y convertirla en un objeto listo para usarse, pero ahi ciertas reglas:

+ El Constructor Inicializa la memoria y la convierte en un objeto utilizable

+ Si no se especifica un constructor:
	- El compilador genera un constructor predeterminado
	- Accesibilidad pública
	- Mismo nombre que el de la clase
	- Sin tipo de retorno, ni siquiera null
	- No espera argumentos
	- Inicializa todos los campos en cero, falso o nulo

+ Si se especifica un constructor
	- Se invoca cuando se inicializa el objeto
	- Se puede sobrecargar

Caracteristicas del Constructor Predeterminado:
+ Todos los campos se inicializan con cero
	- Campos numéricos (int, double, decimal, etc..) se inicializan a cero.
	- Campos bool se inicializan en false.
	- Tipos de referencia(como los objetos) se inicializan en null.
	- Tipos struct se inicializan para contener valores de cero.

+ Accesibilidad pública

No siempre es recomendable el utilizar el constructor predeterminado, por ejemplo, no siempre es bueno el hecho de que el acceso
sea público o quizás sea un problema el hecho de que se inicialicen a 0 ciertos valores, así mismo el código invisible es dificil
de mantener, el código del constructor predeterminado es invisible. Debemos recordar que aunque los constructores son tipos 
especiales de métodos eso no implica que no puedan ser sobrecargados.

Ejemplo:

class Persona
{		
	public Persona( ) //Constructor personalizado
	{
		Nombre = "";
		Apellido = "";
		Console.WriteLine("Objeto Construido");
	}

	public string ObtenerNombreCompleto()
	{
		return ( Nombre+" "+Apellido );
	}

	public string nombre
	{
		get{ return Nombre; }
		set{ Nombre = value ; }
	}

	public string apellido
	{
		get{ return Apellido; }
		set{ Apellido = value; }
	}

	private string	Nombre,Apellido;
}

Algo importante es que podemos modificar la accesibilidad de un constructor.

Constructores privados: Se utilizan para evitar que los objetos se creen a partir de una clase especifíca, imaginemos que desea crear
una clase que provea cierta funcionalidad, pero no desea que los usuarios de esa clase puedan crear objetos con base en ella, bueno
en este caso ponemos metodos y variables estáticas dentro de la clase, en este caso ya no es necesario crear instancias de la clase
(objetos), sin embargo el constructor predeteminado es public por predeterminación, lo cual implica que SI se pueden crear instancias
de esa clase, aunque dichos objetos no tendrán ningún sentido ya que la clase está compuesta únicamente de métodos y variables estáticas,
para evitar esto basta con modificar la accesibilidad del constructor haciendolo private impidiendo que se creen instancias de la clase.
Al declarar un constructor C# no generará ningún constructor y al hacerlo privado evita que se creen instancias de esa clase.

Constructores estáticos: Se emplean para asegurarse de que una clase siempre es inicializada antes de utilizarse, esto nos asegura que
la clase se encuentra en un estado inicial bien definido antes de utilizarse.

Destructores y Terminadores

Provocan que las acciones finales de los objetos sean diferentes y no pueden ser determinadas por recolección de residuos, así mismo
todos los objetos en la plataforma .NET tienen un método de Finalizar, si está presente, la recolección de residuos llamará al Destructor
antes de reclamar la memoria binaria, en C# no se puede llamar o sobrescribir Object.Finalize. Un destructor es un mecanismo de limpieza
de memoria.

Finalización

Cuando el recolector de basura libera los recursos utilizados por un objeto no referenciado, verifica primero si la clase del objeto
no tiene un destructor o método Finalize propio, si la clase cuenta con uno el será invocado antes de reciclar la memoría a la pila. Las
instrucciones escritas en el destructor o método Finalize son únicamente para esa clase.

Destructores

En C# el método Finalize no está disponible de manera directa y no puede ser invocado y/o cancelado, se deberá colocar código para que
sea ejecutado durante la finalización dentro de un estructor, dicho destructor tendrá el nombre de la clase antecedido por el simbolo ~.
Ejemplo:

Puede escribir un destructor para implementar la limpieza de un objeto. En C#,
el método Finalize no está disponible directamente, y no puede invocar o
cancelar el método Finalize. Debe colocar código para que sea ejecutado
durante la finalización dentro de un destructor.

class Persona
{		
	public Persona( ) //Constructor personalizado
	{
		Nombre = "";
		Apellido = "";
		Console.WriteLine("Objeto Construido");
	}

	~Persona()
	{
		Console.WriteLine("Objeto Destruido");
	}

	public string ObtenerNombreCompleto()
	{
		return ( Nombre+" "+Apellido );
	}

	public string nombre
	{
		get{ return Nombre; }
		set{ Nombre = value ; }
	}

	public string apellido
	{
		get{ return Apellido; }
		set{ Apellido = value; }
	}

	private string	Nombre,Apellido;
}
Bueno, eso es todo por este capitulo, espero verlos en el siguiente donde trataré temas como herencia y polimorfismo entre otros...
AVISO: Impartiré unos cursos en linea TOTALMENTE GRATUITOS en la página http://groups.msn.com/MUGVeracruz

 


Anterior Índice Siguiente

ir al índice