InicioArticulos y noticiasBases de datosProgramaciónForosInternetServiciosContratacionEmail
Tutorial C#
Introduccion a C#
Programacion con C#
Fundamentos de C#
Operadores
Instrucciones
Estrcuturas de control.
Definición de clases
Creación de objetos
Herencia y métodos virtuales
Polimorfismo
Ocultación de miembros
Miembros de tipo
Encapsulación
Espacios de nombres
Importación de espacios de nombres
Espacio de nombres distribuidos
Variables y tipos de datos
Tablas unidimensionales
Tablas multidimensionales
La clase System.Array
Cadenas de texto
Constantes
Orden de inicialización de variables
Métodos
Métodos externos
Constructores
Destructores
Propiedades
Indizadores
Redefinición de operadores
Delegados y eventos
La clase MulticastDelegate
Llamadas asíncronas
Implementación interna de los delegados
Eventos
Estructuras
Boxing y unboxing
Constructores de estructuras
Enumeraciones
Interfaces
Excepciones
Otras instrucciones
Atributos
Definición de nuevos atributos
Lectura de atributos en tiempo de ejecución
Atributos de compilación
Pseudoatributos
Código inseguro
Definición de punteros
Manipulación de punteros
Operadores relacionados con código inseguro
Fijación de variables apuntadas
Novedades de C# 2.0
Genéricos
Tipos parciales
Iteradores
Mejoras en la manipulación de delegados
Tipos anulables
Modificadores de visibilidad de bloques get y set
Clases estáticas
Referencias a espacios de nombres
Supresión temporal de avisos
Atributos condicionales
Incrustación de tablas en estructuras
Modificaciones en el compilador
También puedes ver ...
TransactionScope - Simplificando el trabajo con transacciones.
LinQ To SQL - Un ejemplo sencillo
Cómo activar Intelisense (autocompletado) en el Web.config
Leer un campo XML de SQL Server 2005 con C#
TripleDES - Un ejemplo practico en C#
Mostrar un GridView dentro de un DataList
String.Format
Métodos Extensores: INNER JOIN
Problemas en la instalación de Visual Studio 2008-Visual studio web authoring component
Redimiento de VS2008

Afiliados
La Web del programador
MundoProgramacion


 

Constructores

Concepto de constructores

    Los constructores de un tipo de datos son métodos especiales que se definen como miembros de éste y que contienen código a ejecutar cada vez que se cree un objeto de ese tipo. Éste código suele usarse para labores de inicialización de los campos del objeto a crear, sobre todo cuando el valor de éstos no es constante o incluye acciones más allá de una asignación de valor (aperturas de ficheros, accesos a redes, etc.)

    Hay que tener en cuenta que la ejecución del constructor siempre se realiza después de haberse inicializado todos los campos del objeto, ya sea con los valores iniciales que se hubiesen especificado en su definición o dejándolos con el valor por defecto de su tipo.

    Aparte de su especial sintaxis de definición, los constructores y los métodos normales tienen una diferencia muy importante: los constructores no se heredan.

Definición de constructores

    La sintaxis básica de definición de constructores consiste en definirlos como cualquier otro método pero dándoles el mismo nombre que el tipo de dato al que pertenecen y no indicando el tipo de valor de retorno debido a que nunca pueden devolver nada. Es decir, se usa la sintaxis:


<modificadores> <nombreTipo>(<parámetros>)
{
 <código>
}

    Un constructor nunca puede devolver ningún tipo de objeto porque, como ya se ha visto, sólo se usa junto al operador new, que devuelve una referencia al objeto recién creado. Por ello, es absurdo que devuelva algún valor ya que nunca podría ser capturado en tanto que new nunca lo devolvería. Por esta razón el compilador considera erróneo indicar algún tipo de retorno en su definición, incluso aunque se indique void.

Llamada al constructor

    Al constructor de una clase se le llama en el momento en que se crea algún objeto de la misma usando el operador new. De hecho, la forma de uso de este operador es:


new <llamadaConstructor>

    Por ejemplo, el siguiente programa demuestra cómo al crearse un objeto se ejecutan las instrucciones de su constructor:


class Prueba
{
 Prueba(int x)
 {
  System.Console.Write("Creado objeto Prueba con x={0}",x);
 }
 public static void Main()
 {
  Prueba p = new Prueba(5);
 }
}

    La salida por pantalla de este programa demuestra que se ha llamado al constructor del objeto de clase Prueba creado en Main(), pues es:



Creado objeto Prueba con x=5;

 

Llamadas entre constructores

     Al igual que ocurre con cualquier otro método, también es posible sobrecargar los constructores. Es decir, se pueden definir varios constructores siempre y cuando éstos tomen diferentes números o tipos de parámetros. Además, desde el código de un constructor puede llamarse a otros constructores del mismo tipo de dato antes de ejecutar las instrucciones del cuerpo del primero. Para ello se añade un inicializador this al constructor, que es estructura que precede a la llave de apertura de su cuerpo tal y como se muestra en el siguiente ejemplo:


class A
{
 int total;
 
 A(int valor): this(valor, 2);  // (1)
 {
 }
 
 A(int valor, int peso) // (2)
 {
  total = valor*peso;
 }
}

     El this incluido hace que la llamada al constructor (1) de la clase A provoque una llamada al constructor (2) de esa misma clase en la que se le pase como primer parámetro el valor originalmente pasado al constructor (1) y como segundo parámetro el valor 2. Es importante señalar que la llamada al constructor (2) en (1) se hace antes de ejecutar cualquier instrucción de (1)

     Nótese que la sobrecarga de constructores -y de cualquier método en general- es un buen modo de definir versiones más compactas de métodos de uso frecuente en las que se tomen valores por defecto para parámetros de otras versiones menos compactas del mismo método. La implementación de estas versiones compactas consistiría en hacer una llamada a la versión menos compacta del método en la que se le pasen esos valores por defecto (a través del this en el caso de los constructores) y si acaso luego (y/o antes, si no es un constructor) se hagan labores específicas en el cuerpo del método compacto.

     Del mismo modo que en la definición de un constructor de un tipo de datos es posible llamar a otros constructores del mismo tipo de datos, también es posible hacer llamadas a constructores de su tipo padre sustituyendo en su inicializador la palabra reservada this por base. Por ejemplo:


class A
{
 int total;
 
 A(int valor, int peso)
 {
  total = valor*peso;
 }
}
class B:A
{
 B(int valor):base(valor,2)
 {}
}

    En ambos casos, los valores pasados como parámetros en el inicializador no pueden contener referencias a campos del objeto que se esté creando, ya que se considera que un objeto no está creado hasta que no se ejecute su constructor y, por tanto, al llamar al inicializador aún no está creado. Sin embargo, lo que sí pueden incluir son referencias a los parámetros con los que se llamó al constructor. Por ejemplo, sería válido hacer:


A(int x, int y): this(x+y)
{}

Constructor por defecto

    Todo tipo de datos ha de disponer de al menos un constructor. Cuando se define un tipo sin especificar ninguno el compilador considera que implícitamente se ha definido uno  sin cuerpo ni parámetros de la siguiente forma:


public <nombreClase>(): base()
{}

    En el caso de que el tipo sea una clase abstracta, entonces el constructor por defecto introducido es el que se muestra a continuación, ya que el anterior no sería válido porque permitiría crear objetos de la clase a la que pertenece:


protected <nombreClase>(): base()
{}

    En el momento en se defina explícitamente algún constructor el compilador dejará de introducir implícitamente el anterior. Hay que tener especial cuidado con la llamada que este constructor por defecto realiza en su inicializador, pues pueden producirse errores como el del siguiente ejemplo:


class A
{
 public A(int x)
 {}
}
class B:A
{
 public static void Main()
 {
  B b = new B();  // Error: No hay constructor base
 }
}

    En este caso, la creación del objeto de clase B en Main() no es posible debido a que el constructor que por defecto el compilador crea para la clase B llama al constructor sin parámetros de su clase base A, pero A carece de dicho constructor porque no se le ha definido explícitamente ninguno con esas características pero se le ha definido otro que ha hecho que el compilador no le defina implícitamente el primero.

    Otro error que podría darse consistiría en que aunque el tipo padre tuviese un constructor sin parámetros, éste fuese privado y por tanto inaccesible para el tipo hijo.

    También es importante señalar que aún en el caso de que definamos nuestras propios constructores, si no especificamos un inicializador el compilador introducirá por nosotros uno de la forma :base() Por tanto, en estos casos también hay que asegurarse de que el tipo donde se haya definido el constructor herede de otro que tenga un constructor sin parámetros no privado.

Llamadas polimórficas en constructores

    Es conveniente evitar en la medida de lo posible la realización de llamadas a métodos virtuales dentro de los constructores, ya que ello puede provocar errores muy difíciles de detectar debido a que se ejecuten métodos cuando la parte del objeto que manipulan aún no se ha sido inicializado. Un ejemplo de esto es el siguiente:


using System;
public class Base
{
 public Base()
 {
  Console.WriteLine("Constructor de Base");
  this.F();
 }
 
 public virtual void F()
 {
  Console.WriteLine("Base.F");               
 }
}
public class Derivada:Base
{
 Derivada()
 {
  Console.WriteLine("Constructor de Derivada");
 }
 
 public override void F()
 {
  Console.WriteLine("Derivada.F()");
 }
 
 public static void Main()
 {
  Base b = new Derivada();                      
 }
}

    La salida por pantalla mostrada por este programa al ejecutarse es la siguiente:

Constructor de Base

Derivada.F()

Constructor de Derivada

    Lo que ha ocurrido es lo siguiente: Al crearse el objeto Derivada se ha llamado a su constructor sin parámetros, que como no tiene inicializador implícitamente llama al constructor sin parámetros de su clase base. El constructor de Base realiza una llamada al método virtual F(), y como el verdadero tipo del objeto que se está construyendo es Derivada, entonces la versión del método virtual ejecutada es la redefinición del mismo incluida en dicha clase. Por último, se termina llamando al constructor de Derivada y finaliza la construcción del objeto.

    Nótese que se ha ejecutado el método F() de Derivada antes que el código del constructor de dicha clase, por lo que si ese método manipulase campos definidos en Derivada que se inicializasen a través de constructor, se habría accedido a ellos antes de inicializarlos y ello seguramente provocaría errores de causas difíciles de averiguar.

Constructor de tipo

    Todo tipo puede tener opcionalmente un constructor de tipo, que es un método especial que funciona de forma similar a los constructores ordinarios sólo que para lo que se usa es para inicializar los campos static del tipo donde se ha definido.

    Cada tipo de dato sólo puede tener un constructor de tipo. Éste constructor es llamado automáticamente por el compilador la primera vez que se accede al tipo, ya sea para crear objetos del mismo o para acceder a sus campos estáticos. Esta llamada se hace justo después de inicializar los campos estáticos del tipo con los valores iniciales especificados al definirlos (o, en su ausencia, con los valores por defecto de sus tipos de dato), por lo que el programador no tiene forma de controlar la forma en que se le llama y, por tanto, no puede pasarle parámetros que condicionen su ejecución.

    Como cada tipo sólo puede tener un constructor de tipo no tiene sentido poder usar this en su inicializador para llamar a otro. Y además, tampoco tiene sentido usar base debido a que éste siempre hará referencia al constructor de tipo sin parámetros de su  clase base. O sea, un constructor de tipo no puede tener inicializador.

    Además, no tiene sentido darle modificadores de acceso ya que el programador nunca lo podrá llamar sino que sólo será llamado automáticamente y sólo al accederse al tipo por primera vez. Como es absurdo, el compilador considera un error dárselos.

    La forma en que se define el constructor de tipo es similar a la de los constructores normales, sólo que ahora la definición ha de ir prefijada del modificador static y no puede contar con parámetros ni inicializador. O sea, se define de la siguiente manera:


static <nombreTipo>()
{
 <código>
}

   
En la especificación de C# no se ha recogido cuál ha de ser el orden exacto de las llamadas a los constructores de tipos cuando se combinan con herencia, aunque lo que sí se indica es que se ha de asegurar de que no se accede a un campo estático sin haberse ejecutado antes su constructor de tipo. Todo esto puede verse más claro con un ejemplo:


using System;
class A
{
 public static X;
 static A()
 {
  Console.WriteLine("Constructor de A");
  X=1;
 }
}
class B:A
{
 static B()
 {
  Console.WriteLine("Constructor de B");
  X=2;
 }
 
 public static void Main()
 {
  B b = new B();
  Console.WriteLine(B.X);
 }
}

    La salida que muestra por pantalla la ejecución de este programa es la siguiente:


Inicializada clase B

Inicializada clase A

2

    En principio la salida de este programa puede resultar confusa debido a que los primeros dos mensajes parecen dar la sensación de que la creación del objeto b provocó que se ejecutase el constructor de la clase hija antes que al de la clase padre, pero el último mensaje se corresponde con una ejecución en el orden opuesto. Pues bien, lo que ha ocurrido es lo siguiente: como el orden de llamada a constructores de tipo no está establecido, el compilador de Microsoft ha llamado antes al de la clase hija y por ello el primer mensaje mostrado es Inicializada clase B. Sin embargo, cuando en este constructor se va a acceder al campo X se detecta que la clase donde se definió aún no está inicializada y entonces se llama a su constructor de tipo, lo que hace que se muestre el mensaje Incializada clase A. Tras esta llamada se machaca el valor que el constructor de A dió a X (valor 1) por el valor que el constructor de B le da (valor 2) Finalmente, el último WriteLine() muestra un 2, que es el último valor escrito en X.


Inicio | Tutorial C# Métodos externosTutorial C#Destructores Versión para imprimir Foros de consulta


Añadir comentario ... Para preguntar utiliza los foros
Autor:

Título:


Para preguntar utiliza los foros.



Inicio | Tutorial C# Métodos externosTutorial C#Destructores Versión para imprimir

Constructores
Autor: José Antonio González Seco
Visitas: 8208 Fecha de publicación: 13/10/2006
José Antonio es experto en tecnologias Microsoft. Imparte cursos y conferencias en congresos sobre C# y .NET en Universidades de toda España (Sevilla, Barcelona, San Sebastián, Valencia, Oviedo, etc.) en representación de grandes empresas como Microsoft.




18/08/2008 SQL Server 2008
Visitas: 272 | Comentarios: 0 | Archivo: Articulos
Categorias: Transact-SQL
Visitas: 233 | Comentarios: 0 | Archivo: Articulos
Categorias: C#|ADO.NET|LinQ
Visitas: 268 | Comentarios: 1 | Archivo: Articulos
Categorias: Humor
Visitas: 185 | Comentarios: 0 | Archivo: Articulos
Categorias: WCF
Visitas: 162 | Comentarios: 0 | Archivo: Articulos
Categorias: WCF
Visitas: 247 | Comentarios: 0 | Archivo: Articulos
Categorias: ASP.NET
Visitas: 325 | Comentarios: 5 | Archivo: Articulos
Categorias: ASP.NET
Visitas: 5225 | Comentarios: 4 | Archivo: Articulos
Categorias: C#|Transact-SQL|LinQ
Visitas: 658 | Comentarios: 1 | Archivo: Articulos
Categorias: Visual Studio
Visitas: 1551 | Comentarios: 1 | Archivo: Articulos
Categorias: Transact-SQL

Útimos temas recibidos en los foros ...
INSTALAR VB.NET 2005 EXPRESS EN WINDOWS VISTA por tito2701 ... [Visual Basic .NET] 13 05/07/2007
LLave Primaria por Martin ... [ORACLE] 6 22/06/2007
dar formato a mail por Pako ... [C#] 4 16/03/2007
MP3 en Windows Vista por Alf ... [Actualidad] 3 20/04/2008
autogenerado por mily ... [Visual Basic .NET] 0 27/08/2008
Instalación de Oracle por Tatatn ... [ORACLE] 0 27/08/2008
For Update, existe alguna otra forma? por Sus ... [ORACLE] 1 27/08/2008
Cambiar el color del nodo de un treeview por Moises Bermdez Contreras ... [ASP.NET] 3 15/10/2007
librerias dinamicas por lupitaor ... [C#] 2 26/08/2008
Vista SQL 2005 limitada por Factor ... [SQL] 2 26/08/2008
Combobox por Corven ... [C#] 4 10/07/2008
Datagrid view por Jazmin ... [ASP.NET] 5 25/08/2008

Access CGI JSP ORACLE UNIX
Actualidad HTML/DHTML/XHTML LINUX PHP Visual Basic .NET
ASP ISAPI MS DOS Power Builder Visual Basic 6.0
ASP.NET Java mySQL SQL WIN 98/NT/2000/XP
C# JavaScript Opinion SQL Server

devjoker  Te recomendamos además ...
16/04/2004 C# y como crear instancias de clases desde un tipo String utilizando .NET FrameWork.
03/10/2006 Creación de objetos    forma parte de...Tutorial C#
12/01/2006 Introducción a PLSQL    forma parte de...Tutorial PL/SQL
11/03/2008 Problemas en la instalación de Visual Studio 2008-Visual studio web authoring component
13/10/2006 Constructores    forma parte de...Tutorial C#
03/10/2006 Fundamentos de C#    forma parte de...Tutorial C#
14/01/2008 Novedades Visual Studio 2008 DataSet en 3 capas o MVC
19/08/2006 Vitualización. Maquinas Virtuales.
28/11/2006 Definición de nuevos atributos    forma parte de...Tutorial C#
28/08/2006 Ubuntu en VmWare con VMware Tools

 

Encuesta
¿A que perfil te adaptas mejor?
[Ver] [Votar]