C# ClassConstructor: Notas sobre constructores

Aquí van algunos ejemplos más avanzados con breves anotaciones sobre constructores de clases.

Los constructores son métodos especiales que se invocan cuando se instancia una clase, un método constructor nunca debe retornar nada (no es necesario definir el tipo de retorno en su declaración). Siempre que en una clase e define explicitamente un constructor el constructor por omisión o implícito es reemplazado por este.

El operador new crea un objeto nuevo y a continuación se invoca al constructor de la clase.

Constructor sobrercargadado

El método constructor también puede ser sobrecargado con varios métodos, con el mismo nombre (el de la clase) pero con diferentes parámetros:

c1

Llamadas entre constructores

A pesar de que pueda parece raro un constructor puede llamar a otro también. En el siguiente ejemplo primero se llama al constructor sin parámetros. Podemos usarlo para instanciar objetos comunes en el constructor sin parámetros y llamarlo desde constructores que aceptan diferentes tipos de inicializaciones especificas,

c2

Uso de la clase clsCamion:

c3

c4

También podemos pasar parámetros de un constructor a otro:

c5

 

Destructores

C# tiene un recolector de basura, el propio framework se ocupa de liberar los objetos que ya no usamos. En algunos casos tal vez no interese realizar una “limpieza” manual, para la clase clsCamion de arriba tendría el siguiente aspecto:

c6

Constructor de la clase base

Usando la palabra clave base podemos invocar el constructor de la clase base cuando usamos la herencia.

c7

 

 

Entradas relacionadas (clases):

Enlaces externos:

C# GetSetAccessDesc: Ejemplos descriptores de acceso con get/set

Algunos ejercicios básicos para entrenar con los descriptores de acceso get y set. Algunos usos que se me ocurren para get/set son: Validar los datos antes de permitir un cambio, obtener datos de otro origen de forma transparente (una base de datos por ejemplo), realizar una acción cuando se modifican datos (provocar un evento o cambiar el valor de otros campos relacionados).

clsDate: Control asignación de valores mediante set

En este ejemplo Month se declara como propiedad para asegurarnos que se establece un valor correcto para el mes comprendido entre 1 y 12.

getset1

Si por ejemplo tratamos de establecer un mes 15 no da error pero el valor no se establece y se mantiene a 0 (valor por defecto).

getset2

clsPerson: Descriptor acceso sólo get

El descriptor de acceso get debe devolver el valor de la propiedad, si solo definimos get la propiedad será solo de lectura, si intentamos modificar su valor el compilador no lo permitirá.

getset3

Uso de la clase clsPerson:

getset4

Cuando llamamos a Console.WriteLine entra en acción el descriptor get.

clsTrabajador: get para controlar si un campo es consistente

Podemos usar el descriptor de acceso get para devolver el valor de un campo haciéndolo consistente, en este caso empleamos un operador condicional ternario para comprobar si la cadena es null, en ese caso retornamos una cadena “NA” para indicar que el valor no se ha inicializado correctamente aún.

getset5

Entradas relacionadas:

C# Caja: Ejercicio con indexadores

En la entrada “C# class LibroCalificaciones: Acceso a propiedades con descriptores de acceso get / set” se estudio como se pueden emplear los descriptores de acceso get/set para controlar el acceso a los datos privados de una clase, por ejemplo:

idx1

Dentro de una clase podemos almacenar listas de datos  en arrays pòr ejemplo, podemos acceder a estos miembros usando indexadores, estos permiten el acceso indexado a las listas de elementos usando su índice numérico o usando un índice basado en nombres.

modificadorAcceso tipoRetorno this[ TipoIndice1 nombre1, TipoIndice2 nombre2, ...]
{
    get
    {
          // usa nombre1, nombre2...para acceder a datos
    }
    set
    {
          // usa nombre1, nombre2...para acceder a datos
    }
}

Vamos a demostrar su uso con una clase llamada clsCaja que contiene un array con 3 miembros privados de tipo double con las propiedades longitud, altura, anchura. Para poder indexar por nombre del atributo al array de doubles también definiremos un arreglo de string con los nombres de las propiedades.

idx2

Ahora siguiendo la sintaxis de arriba vamos a definir dos indexadores, el primero más sencillo de entender permite acceder a los elementos del arreglo usando un índice numérico de tipo int.

idx5

El segundo indexador utiliza un índice string que representa el nombre de la medida que queremos obtener (“longitud”,”altura”,”anchura”).

Cada indexador devuelve -1 si su descriptor de acceso get detecta un subíndice no valido. Lo mismo hace set set si tratamos de establecer un elemento no valido.

idx6

Ahora ya podemos usar los indexadores:

idx7

Entradas relacionadas:

Ejercicio completo


/*
* Created by SharpDevelop.
* User: i.landajuela
* Date: 21/12/2016
* Time: 18:34
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;

namespace Indexadores
{

//Ejemplo de uso de descriptores de acceso get/set para controlar
//el acceso a miembros privados de la clase
class TimePeriod
{
private double _seconds;
public double Seconds
{
get { return _seconds; }
set { _seconds = value; }
}
}

//-----------------------------------------------------------------

public class clsCaja
{
private string[] nombres = {"longitud","altura","anchura"};
private double[] medidas = new double[3];

public clsCaja(double longitud, double anchura, double altura)
{
medidas[0]=longitud;
medidas[1]=anchura;
medidas[2]=altura;
}

//indexador para acceder a medidas por el número de indice entero
public double this[int indice]
{
get
{
//valida indice a obtener
if ((indice<0) || (indice >= medidas.Length))
{
return -1;
}
else
return medidas[indice];
} //fin get
set
{
if (indice>=0 && indice < medidas.Length)
medidas[indice] = value;
} //fin set

} // fin de indexador numérico

public double this[string nombre]
{
get
{
//localiza elemento a obtener
int i=0;
while((i<nombres.Length)&&(nombre!=nombres[i]))
i++;
return (i==nombres.Length) ? -1 : medidas[i];
}
set
{
int i=0;
while((i<nombres.Length)&&(nombre!=nombres[i]))
i++;
if(i!=nombres.Length)
medidas[i] =value;
}
} //fin de indexador string

}


class Program
{
public static void Main(string[] args)
{
clsCaja objCaja = new clsCaja(10,20,30);


objCaja[0] = 80;
Console.WriteLine("caja[\"longitud\"]={0}",objCaja["longitud"]);
objCaja["longitud"] = 90;
Console.WriteLine("caja[\"longitud\"]={0}",objCaja["longitud"]);

Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}

C# PruebaThis: Referencia a los miembros del objeto actual con “this”

Los objetos pueden acceder a una referencia a si mismo usando la palabra clave this. Hace referencia a la instancia actual de la clase. Puede ser usado con el fin de evitar ambigüedades como en este ejemplo:

t1

El constructor de la clase TiempoSimple recibe tres argumentos int para inicializar el objeto. En el constructor usamos nombres de parámetros de entrada idénticos a los nombres de las variables de instancia de la clase (hora,minuto,segundo). Usando this podemos diferenciar entre unas y otras.

El método público y accesible por el cliente  CrearString devuelve un objeto string creado usando la referencias this de forma explícita e implícita.

t2

Ahora vemos como podemos usar la clase, creamos una instancia de la clase  TiempoSimple y invocamos a su constructor.  Luego invocamos al método CrearString del objeto y muestra los resultados en pantalla.

t3

Ejemplo completo:


 /*
  * Created by SharpDevelop.
  * User: i.landajuela
  * Date: 20/12/2016
  * Time: 17:15
  * 
  * To change this template use Tools | Options | Coding | Edit Standard Headers.
  */
 using System;
 
 namespace PruebaThis
 {
     
     public class TiempoSimple
     {
         private int hora=0,minuto=0,segundo=0;
         
         public TiempoSimple(int hora,int minuto, int segundo)
         {
             this.hora = hora;
             this.minuto = minuto;
             this.segundo = segundo;
         }
         
         public string CrearString()
         {
             return string.Format("{0:24}: {1}\n{2,24}: {3}",
                                  "this.AStringUniversal()",this.AStringUniversal(),
                                  "AStringUniversal()",AStringUniversal());
         }
         
         public string AStringUniversal()
         {
             return string.Format("{0:D2}:{1:D2}:{2:D2}",
                                 this.hora,this.minuto,this.segundo);
         }
 
     }
     
     class Program
     {
         public static void Main(string[] args)
         {
             TiempoSimple objT1 = new TiempoSimple(23,24,12);
             
             Console.WriteLine(objT1.CrearString());
                         
             Console.Write("Press any key to continue . . . ");
             Console.ReadKey(true);
         }
     }
 } 

 

 

C# Clases PruebaTiempo1: Clase básica

El siguiente ejemplo consta de una clase Tiempo1, contiene 3 variables privadas (private) de instancia de tipo entero (int):  hora, minuto y segundo. Representan la hora en formato de tiempo universal (formato reloj de 24 horas donde los valores de cada miembro se encuentran en un rango de 0 a 23).  La clase Tiempo1 contiene los métodos public accesibles desde otras clases:  EstablecerTiempoAStringUniversalToString. El constructor no se declara por lo que el compilador emplea uno predeterminado. Cada variable de instancia recibe de forma implícita el valor 0 por defecto para un int (podemos hacer la prueba modificando el acceso a las variables con public e imprimiendo su valor por consola sin inicializar), aunque también podemos inicializarlas de la misma forma que una variable local.

tiempo1_1

El método  EstablecerTiempo es public y recibe tres parámetros de entrada que permiten establecer la hora. Dentro del método se evalúan si los datos de entrada son válidos, si el valor de entrada no está dentro del rango 0 a 23 para la hora se asigna un 0 por defecto (lo mismo para minutos y segundos si no están comprendidos entre 0 y 60). Para ello se emplea un operador condicional ternario (ver referencia operador ? en MSDN). De esta forma nos aseguramos que a pesar de que introduzcamos datos no válidos el valor es consistente.

tiempo1_3

El método  AStringUniversal no recibe argumentos y retorna un objeto de tipo string en formato de hora universal con forma “HH:MM:SS”.  utilizamos el método static Format del objeto string para darle la forma deseada, “{0:D2}:{1:D2}:{2:D2}”  con 2 dígitos y donde sea necesario un cero a la izquierda.

tiempo1_4Con “D2” indicamos por ejemplo que se trata de un decimal entero (“D”), el 2 lo usamos como especificador de precisión, rellenará la cadena final con ceros por la izquierda hasta que el número alcance 2 dígitos de longitud (ver MSDN Cadenas con formato numérico estándar).

El método  ToString no recibe argumentos y retorna un objeto String en formato AM/PM. Cabe destacar la palabra clave  override o sobrecarga, todos los objetos en C# tienen un método ToString que devuelve una representación en forma de cadena (también otros como GetType o Equals). Usando el modificador override ampliamos su implementación original.

tiempo1_5

Por ejemplo: Cuando declaramos un objeto de tipo String el método ToString se llama de forma implícita cuando usamos Console.Write con el objeto.

tiempo1_6

Uso de la clase Tiempo1

Después de declararla podemos usarla de las siguientes formas:

tiempo1_7

 


/*
* Created by SharpDevelop.
* User: i.landajuela
* Date: 20/12/2016
* Time: 10:54
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;

namespace PruebaTiempo1
{
//Clase tiempo para mantener la hora
class Tiempo1
{
//3 variables privadas de instancia private de tipo int
//Se inicializan a 0 por defecto
private int hora; //0 - 23
private int minuto; //0 - 59
private int segundo; //0-59

//Establece un nuevo valor de tiempo usando la hora universal, controla
//lo datos invalidos haciendolos consistentes
public void EstablecerTiempo(int h, int m, int s)
{
this.hora = ( (h>=0 && h<24) ? h : 0 );
this.minuto = ( (m>=0 && m<60) ? m : 0 );
this.segundo = ( (s>=0 && s<60) ? s : 0 );
}

//Convierte en string en formato universal HH:MM:SS
public string AStringUniversal()
{
return string.Format("{0:D2}:{1:D2}:{2:D2}",
this.hora,this.minuto,this.segundo);
}

//Convierte en string, en formato hora estándar H:MM:SS AM o PM)
//Sobrecargamos la función ToString
//
public override string ToString()
{
return string.Format("{0}:{1:D2}:{2:D2}:{3}",
((hora==0||hora==12)? 12 : (hora % 12)),
this.minuto,
this.segundo,
( hora < 12 ? "AM" : "PM"));
}
}


class Program
{
public static void Main(string[] args)
{
Tiempo1 objT1;

objT1.EstablecerTiempo(23,30,01);

Console.WriteLine("Como hora universal: {0}.",objT1.AStringUniversal());
Console.WriteLine("Usando método .ToString sobrecargado: {0}.",objT1.ToString());

Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}

&nbsp;

C# class LibroCalificaciones: Acceso a propiedades con descriptores de acceso get / set

csharp10

Diagrama clase con  plugin gliffy para Chrome

csharp9

Los descriptores de acceso get / set de para un propiedad o atributo son muy útiles para controlar el acceso a los mismos. Es muy similar a crear un método o función pública para acceder a una propiedad privada y poder controlar los valores de entrada por ejemplo.

Vamos a crear una solución llamada PruebaLibroCalificaciones, a continuación creamos una nueva clase LibroCalificaciones.cs en una subcarpeta classes del proyecto, en el futuro estas práctica de guardar las clases en una subcarpeta nos va a resultar muy útil para mantener el código ordenado. Desde el programa principal en Program.cs debemos añadir la siguiente línea de código para poder acceder a las clases anidadas dentro de la carpeta classes:


using PruebaLibroCalificaciones.classes;

Nuestra clase de prueba  LibroCalificaciones va a tener una propiedad privada de tipo string con nombre nombreCurso (solo accesible desde los métodos de la propia clase pero no desde fuera). A continuación definimos otra variable publica de tipo string  NombreCurso (ojo que no es exactamente el mismo nombre)  para definir los descriptores de acceso get y set a la propiedad nombreCurso.

Program.cs:
 using System;
 using PruebaLibroCalificaciones.classes;
 
 
 namespace PruebaLibroCalificaciones
 {
     class Program
     {
         public static void Main(string[] args)
         {            
             LibroCalificaciones libro1 = new LibroCalificaciones();
             
             libro1.NombreCurso = "C Sharp";
             libro1.MostrarMensaje();
                 
             Console.Write("Press any key to continue . . . ");
             Console.ReadKey(true);
         }
     }
 }</pre>

LibroCalificaciones.cs


using System;

namespace PruebaLibroCalificaciones.classes
{
/// <summary>
/// Description of LibroCalificaciones.
/// </summary>
public class LibroCalificaciones
{
private string nombreCurso;
public string NombreCurso
{
get
{
return nombreCurso;
}
set
{
nombreCurso = value;
}
}

public void MostrarMensaje()
{
Console.WriteLine("Bienvenido al curso de "+NombreCurso);
}

public LibroCalificaciones()
{
}
}
}