Métodos Parte 1 – Constructores en C#

Métodos Parte 1 – Constructores en C#

Constructores en C#

En esta serie de artículos quiero cubrir los diferentes tipos de métodos que están presentes en .NET framework. El primer tipo de métodos que quiero cubrir son los constructores en C#. Sí, los constructores también son los métodos cuyos nombres y tipos de devolución se han reducido solo al nombre de la clase. Los constructores en C# se han utilizado para construir los valores de los miembros de datos, así como las variables miembro de la clase. En este artículo discutiré todos los puntos importantes relacionados con los constructores. Comencemos uno por uno.

Proyecto de código

Antes de comenzar el artículo, es posible que desee leer sobre el tipo de referencia y los tipos de valor aquí.

  1. Como ya se mencionó, los constructores son los métodos que permiten inicializar instancias del tipo y establecer los miembros de datos locales en sus valores iniciales o predeterminados. Por favor, eche un vistazo al código de ejemplo a continuación
       public class MyClass
       {
           private int intVar = 5;
       }
    

    Ahora mire el código IL que he generado usando ILDasm.exe. Aquí podemos ver claramente que se crea un constructor para MyClass que carga las intVar valor en la memoria y llama al constructor de la clase base después de eso. También podemos ver que el constructor se define como método.

  2. Siempre es un punto de confusión para algunas personas si primero se llama al constructor o primero se asigna la memoria. Es importante tener en cuenta que para crear las instancias del tipo, los constructores siempre se llaman primero para calcular la cantidad de memoria requerida para los campos de datos de la instancia. Antes de llamar al constructor del tipo, se juzga el espacio de memoria para ese tipo. Después de realizar todos estos procesos, la memoria se asigna para el tipo en la memoria del montón.
  3. El constructor como método no se puede heredar.
  4. Como los constructores no se pueden heredar, es por eso que las palabras clave virtuales, nuevas, anuladas, selladas o abstractas no están permitidas para los constructores.
  5. Si el usuario no ha definido ningún constructor, en ese caso el compilador define automáticamente el constructor sin parámetros predeterminado como se menciona en el punto 1, que llama al constructor sin parámetros de la clase base. De esta manera, el constructor de clase de System.Object es el que se llama primero. He creado el siguiente fragmento de código para comprender mejor los constructores y la forma en que se inicializan
    public class MyBaseClass
        {
            public int myLocalVar = 10;
    
            public MyBaseClass()
            {
                Console.WriteLine("Base Class myLocalVar Value: " + myLocalVar);
                myLocalVar = 20;
                Console.WriteLine("Base Class Constructor's myLocalVar Value: " + myLocalVar);
            }
        }
    
        public class MyDerivedClass : MyBaseClass
        {      
            public MyDerivedClass()
            {
                myLocalVar = 30;
                Console.WriteLine("Base class's myLocalVar value :" + myLocalVar);
                Console.Read();
            }
        }

    El código anterior muestra la secuencia en la que se inicializan los constructores. Si creo una instancia de la clase derivada como se muestra a continuación. Este es también el escenario de cómo se comportan los constructores en caso de herencia.

                MyDerivedClass derivedClass = new MyDerivedClass();
    
    

    La salida del código anterior será como se muestra a continuación,

    Como podemos ver en el resultado, la variable miembro de la clase base es la que se inicializa en primer lugar y luego se llama al constructor de la clase base y luego se llama al constructor de la clase derivada.

  6. Si la clase es abstracta en ese caso, el constructor predeterminado tiene accesibilidad protegida; de lo contrario, el constructor tiene accesibilidad pública.
  7. Si no hay ningún parámetro menos constructor en la clase base, en ese caso el compilador generó el error de tiempo de compilación como se muestra en el código a continuación.

    El error se puede resolver llamando al constructor de clase base explícitamente como se muestra a continuación,

    public class MyDerivedClass : MyBaseClass
        {
            public MyDerivedClass(int localvar):base(localvar)
            {
    
            }
        }
    
  8. Los mismos constructores de clase pueden llamarse usando la palabra clave this. Este escenario puede ser útil para inicializar todos los campos de instancia en un solo constructor, si tenemos varios constructores definidos en una clase. Siempre es recomendable inicializar todos los campos de instancia en un solo parámetro menos constructor en lugar de inicializarlos al mismo tiempo que la declaración (que se conoce mejor como inicialización de campo de instancia en línea). El ejemplo de código es el siguiente.
        public class MyClass
        {
            private int intVar;
            private string stringvar;
            private double doubleVar;      
    
    
            public MyClass()
            {
                intVar = 5;
                stringvar = "Hello";
                doubleVar = 3.14;
            }
    
            public MyClass(int x):this()
            {
    
            }
    
            public MyClass(string y):this()
            {
    
            }
        }
    
  9. Los constructores sin parámetros no están permitidos para los tipos de valor en C#, como podemos ver en el siguiente código, se genera un error de tiempo de compilación cada vez que intentamos hacerlo

    Pero definitivamente podemos tener constructores con parámetros para los tipos de valor y estos constructores se llamarán solo si los llamamos explícitamente; de ​​lo contrario, los tipos de valor se asignan con valores de 0 o NULL.

    Ejemplo de código para estructura de tipo de valor.

        public struct MyStruct
        {
            public MyStruct(int x)
            {
    
            }
        }
    

Después de hablar sobre todos los conceptos sobre los constructores, quiero hablar sobre los diferentes tipos de constructores que podemos crear en C#.

  1. Constructores estáticos:

    Como sabemos, los constructores de instancias se usan para inicializar los miembros de datos de una clase, de manera similar, los constructores de tipo (estáticos) se usan para inicializar los miembros de datos estáticos y las variables de miembros del tipo, es decir, se usan para establecer el estado inicial de un tipo y no es una instancia.
    Por defecto no hay constructores de tipo definidos dentro de un tipo, y si queremos tener un constructor de tipo no podemos tener más de uno en un solo tipo. Además, los constructores de tipo no toman parámetros.
    Los constructores de tipo (estáticos) para el tipo de referencia y los tipos de valor se definen a continuación

        public class MyClass
        {
            static MyClass()
            {
    
            }
        }
    
    
        public struct MyStruct
        {
            static MyStruct()
            {
    
            }
        }
    

    Como podemos ver en el fragmento de código anterior, los constructores de tipos no tienen modificadores de acceso. Estos constructores son privados de forma predeterminada para evitar que cualquier código escrito por el desarrollador los llame.

    Si los constructores estáticos fueran públicos, esto podría haber causado muchos errores sutiles en el código. Lo primero es que es CLR el que llama a estos constructores estáticos mientras hace referencia a la primera instancia de la clase.

    La llamada del constructor de tipos es algo complicado que me gustaría discutir aquí. Cuando el compilador Just in Time (JIT) está compilando un código de métodos, ve a qué tipos se hace referencia en el código y si alguno de los tipos define un constructor de tipo (estático). El compilador verifica si el constructor estático del tipo ya se ejecutó para este dominio de aplicación. Si el constructor nunca se ha ejecutado, el compilador llama al constructor estático e inicializa todos los campos estáticos de la clase. Si el constructor estático ya ha sido llamado en ese caso, el compilador nunca lo volvió a ejecutar.

    ¿Qué pasa si muchos subprocesos quieren ejecutar el mismo constructor de tipo?

    En este tipo de escenario en el que varios subprocesos ejecutan el mismo método al mismo tiempo que hace referencia a nuestra clase con un constructor estático, CLR garantiza que el constructor estático se ejecute solo una vez por AppDomain. Para garantizar esto, cuando se llama a un constructor estático, el subproceso de llamada adquiere un bloqueo de sincronización de subprocesos mutuamente excluyente. Entonces, si varios subprocesos intentan llamar simultáneamente al constructor estático de un tipo, solo un subproceso adquirirá el bloqueo y los demás subprocesos se bloquearán. El primer hilo ejecutará el constructor estático. Después de que el primer subproceso abandone el constructor, los subprocesos en espera se despertarán y verán que el código del constructor ya se ejecutó y no volverán a ejecutar el código.

    Como ejemplo para utilizar los constructores estáticos, me gustaría mostrar un fragmento de código que es básicamente una clase de contenedor de datos, que funciona en la lista de datos. Como seguridad, todas las funciones de esta clase que trabajan en esta lista de datos deben tener una lista precargada. Esto se puede lograr teniendo un constructor estático en la clase. Por favor, eche un vistazo al código a continuación.

    public static class DataContainer
        {
            private static IList list;
    
            static DataContainer()
            {
                list = new List() { 1, 2, 3};
            }
    
            public static void AddItem(int intvar)
            {
                list.Add(intvar);
            }
    
            public static int RetrieveItemAt(int position)
            {
                if (list.Count > position)
                    return list[position];
                else
                    return -1;
            }
         }
    

    En ausencia del constructor estático, debemos haber usado un método que debería inicializar y llenar la lista, en ese caso hay buenas posibilidades de llamar a esa función.

  2. Constructores privados

    Los constructores privados en C# se usan en una clase para evitar que se cree una instancia de la clase desde fuera de la clase.

    Uno de los escenarios en los que quiero usar un constructor privado es donde quiero que todo mi código de inicialización esté presente en un solo constructor y no permitir que ese constructor llame desde afuera esa clase como se muestra en el siguiente código.

        public class MyClass
        {
            private int intVar;
            private string stringvar;
            private double doubleVar;
    
    
            private MyClass()
            {
                intVar = 5;
                stringvar = "Hello";
                doubleVar = 3.14;
            }
    
            public MyClass(int x)
                : this()
            {
    
            }
    
            public MyClass(string y)
                : this()
            {
    
            }
         }
    

    Otro caso en el que usamos con frecuencia constructores privados son las clases singleton. Estas son las clases que mantienen solo una única instancia de sí mismas en toda la aplicación. Los constructores privados nos permiten hacerlo como se muestra en el siguiente código.

        public class Singleton
        {
            private static Singleton _Singleton;
            private Singleton()
            {
    
            }
    
            public static Singleton GetInstance()
            {
                if (_Singleton == null)
                    _Singleton = new Singleton();
    
                return _Singleton;
            }
        }
    

Eso es todo lo que quiero discutir sobre los constructores. Espero que este artículo te haya ayudado a entender acerca de los constructores y sus conceptos. Hágame saber sus pensamientos sobre el artículo o si me he perdido algo para incluir en este artículo.