Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprende Herencia | Principios de POO
C# Más Allá de lo Básico

bookHerencia

En la sección anterior, revisaste el concepto de clases derivadas. Esta característica de una clase para heredar propiedades de otra clase se denomina Herencia.

Aunque ya conoces el concepto de Herencia, en esta ocasión lo abordarás de una manera un poco más completa para comprenderlo en mayor profundidad.

Como repaso rápido, a continuación se muestra un ejemplo de Herencia:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344
#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kilogram (1 kg = 2.2 pounds) } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Cat myCat = new Cat(); Dog myDog = new Dog(); myCat.speak(); myDog.speak(); } }
Note
Nota

La directiva #pragma warning disable "warning code" puede utilizarse para deshabilitar ciertas advertencias del compilador. Generalmente se desaconseja deshabilitar advertencias, ya que ignorarlas puede provocar comportamientos inesperados en el programa.

El código anterior contiene una clase padre llamada Mammal y dos clases derivadas llamadas Cat y Dog.

Observa que ninguna de las clases tiene un constructor definido explícitamente, lo que significa que estas clases utilizarán un constructor predeterminado cuando se cree un objeto.

Note
Nota

Un constructor predeterminado es proporcionado automáticamente por el lenguaje de programación si una clase no tiene ningún constructor definido explícitamente. Un constructor predeterminado es básicamente un constructor vacío que no contiene ningún código, por ejemplo public className() {} es cómo podría verse un constructor predeterminado. Dado que no inicializa ningún atributo explícitamente, todos los atributos toman valores predeterminados - también llamados valores cero.

Vamos a crear manualmente un constructor para la clase Mammal, que inicializa un objeto Mammal con algunos datos:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }

Si intentas compilar este programa, aparecerán algunos errores en la consola. Para comprender esos errores, primero necesitas entender dos conceptos importantes relacionados con los constructores.

El primero es que, una vez que defines explícitamente un constructor para una clase, esa clase ya no tiene un constructor predeterminado, por lo que el constructor definido explícitamente se convierte en el constructor principal de esa clase, que en este caso es:

index.cs

index.cs

copy
12345
public Mammal(int age, float weight) { this.age = age; this.weight = weight; }

Por lo tanto, al crear un nuevo objeto, siempre se deben pasar los argumentos requeridos del constructor en el orden correcto:

index.cs

index.cs

copy
1234567
// Incorrect ways to create 'Mammal', will show an error Mammal m1 = new Mammal(); Mammal m1 = new Mammal(10); Mammal m1 = new Mammal(42.0f); // Correct way to create 'Mammal', will execute fine. Mammal m1 = new Mammal(10, 42.0f);

En segundo lugar, las clases derivadas también pueden tener constructores; sin embargo, antes de que se llame al constructor de una clase derivada, también se llama al constructor de la clase base (padre):

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132333435
#pragma warning disable CS0169 // To disable some unnecessary warnings, using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg // No attribute is initialized explicitly in this constructor // Hence, all attributes will take up "zero" values // It is similar to a "default" constructor except it outputs a message public Mammal() { Console.WriteLine("Mammal Constructor Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog Constructor Called"); } } class ConsoleApp { static void Main() { Dog myDog = new Dog(); } }

Al ejecutar este código, se observa que el método WriteLine() del constructor de 'Mammal', que es la clase padre, se llama automáticamente. Esto significa que existe una regla según la cual el constructor de la clase base (también llamado constructor base) siempre se invoca antes que el constructor de la clase derivada.

Esta regla también se aplica en el caso de la herencia multinivel:

En el diagrama anterior, el constructor de Kitten llama al constructor de Cat antes que al suyo propio, sin embargo, dado que Cat también es una clase derivada, llama al constructor de Mammal antes que al suyo, y Mammal llama al constructor de Animal antes que al suyo, por lo tanto, en general, el primer constructor que se ejecuta es el constructor de la superclase — que es el constructor de la clase Animal y luego continúa hacia abajo desde allí.

Si el constructor de la clase padre no recibe ningún argumento, el compilador lo llama automáticamente, esta es la razón por la que el constructor de 'Mammal' en el ejemplo anterior fue llamado automáticamente. Sin embargo, veamos nuevamente el código defectuoso:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243
using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }

En el código anterior se obtienen dos errores que básicamente significan que no se han llamado manualmente los constructores base — ya que requieren algunos argumentos, es necesario llamarlos manualmente. La sintaxis básica para llamar manualmente al constructor de la clase padre es la siguiente:

index.cs

index.cs

copy
12345678910
class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }

Ejemplo:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132
using System; class ExampleParentClass { int value1; int value2; public ExampleParentClass(int value1, int value2) { this.value1 = value1; } } class ExampleDerivedClass : ExampleParentClass { int value3; // The value1 and value2 arguments are passed to the base class's contructor public ExampleDerivedClass(int value1, int value2, int value3) : base (value1, value2) { this.value3 = value3; } } class ConsoleApp { static void Main() { var testObject = new ExampleDerivedClass(5, 7, 9); } }

Usando esta sintaxis, puedes pasar todos los datos requeridos al constructor de Mammal a través de los constructores de Cat y Dog para corregir el error que obtenías antes:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public Cat(int age, float weight, string furPattern) : base(age, weight) { this.furPattern = furPattern; } public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); // Creating a "Dog" object with some data Dog d1 = new Dog(10, 42.5f, "Dobermann"); Console.WriteLine("Executed Successfully"); } }

Otra característica importante de los constructores es que puedes sobrecargar constructores de la misma manera que sobrecargas cualquier otro método. Puedes crear múltiples constructores con diferente número de argumentos:

index.cs

index.cs

copy
12345678910111213141516171819202122232425
class Mammal { int age; float weight; // kg // 1st constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor } // 2nd constructor public Mammal(int age) { this.age = age; } // 3rd constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; } }

En este caso, la clase Mammal tiene 3 constructores. Por lo tanto, es posible inicializar o crear un objeto mamífero de 3 maneras diferentes y el compilador elegirá qué constructor llamar según la cantidad y el tipo de argumentos:

index.cs

index.cs

copy
1234
// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);

Esto también significa que puedes llamar a cualquiera de los 3 constructores desde los constructores de la clase derivada. Por ejemplo, todos estos son válidos:

index.cs

index.cs

copy
123456789101112131415161718
// Using 3rd base constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } // Using 2nd base constructor public Dog(int age, string breed) : base(age) { this.breed = breed; } // Using 1st base constructor // If the base constructor has no arguments then it is automatically called (similar to the default constructor), so we don't necessarily need to write 'base()' public Dog(string breed) { this.breed = breed; }

Vamos a unir los dos fragmentos anteriores y agregar algunas sentencias Console.WriteLine para ver en qué orden se ejecutan los constructores y observar los resultados de manera práctica:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
using System; class Mammal { int age; float weight; // kg // 1st Constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor // The attributes are initialized with zero values Console.WriteLine("Mammal - Constructor 1 Called"); } // 2nd Constructor public Mammal(int age) { this.age = age; Console.WriteLine("Mammal - Constructor 2 Called"); } // 3rd Constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; Console.WriteLine("Mammal - Constructor 3 Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog - Constructor 1 Called"); } // Using 1st Mammal constructor // We don't necessarily need to write 'base()' in this case // It automatically finds and calls the constructor with no arguments public Dog(string breed) { this.breed = breed; Console.WriteLine("Dog - Constructor 2 Called"); } // Using 2nd Mammal constructor public Dog(int age, string breed) : base(age) { this.breed = breed; Console.WriteLine("Dog - Constructor 3 Called"); } // Using 3rd Mammal constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; Console.WriteLine("Dog - Constructor 4 Called"); } public void speak() { Console.WriteLine("Woof!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object using different constructors Mammal m1 = new Mammal(10, 42.0f); Mammal m2 = new Mammal(10); Mammal m3 = new Mammal(); Console.WriteLine("----------"); // Seperator, for ease of reading output // Creating a "Dog" object using different constructors Dog d1 = new Dog(10, 42.0f, "Dobermann"); Console.WriteLine(""); Dog d2 = new Dog(10, "Dobermann"); Console.WriteLine(""); Dog d3 = new Dog("Dobermann"); Console.WriteLine(""); Dog d4 = new Dog(); } }

Ahora que conoces las diferentes características de la herencia, también debes saber cómo y cuándo utilizarlas correctamente. A continuación, se presentan algunos aspectos a tener en cuenta al considerar una estructura de clases basada en herencia:

Equilibrio entre simplicidad y flexibilidad: La sobrecarga de constructores permite tener varios constructores que aceptan diferentes tipos de argumentos, pero abusar de esto puede hacer que el código sea más complejo y difícil de mantener. Es una buena práctica mantener el código de la clase breve, conciso y conveniente. Evita crear demasiados constructores para una clase para mantener un equilibrio entre simplicidad y flexibilidad.

Mantener los constructores simples: Los constructores deben ser responsables principalmente de inicializar un objeto con datos básicos. Es una buena práctica evitar procesamiento innecesario y lógica compleja dentro de un constructor. Si se necesita algún cálculo o lógica, es preferible crear un método separado para ello.

;Mala práctica:;

index.cs

index.cs

copy
123456789101112131415161718192021222324252627
class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; if (accountType == "Savings") { // Plus 1 Percent this.balance = balance + balance * 0.01; } else if (accountType == "HighYieldSavings") { // Plus 5 percent this.balance = balance + balance * 0.05; } else { this.balance = balance; } } }

Buena práctica:

index.cs

index.cs

copy
123456789101112131415161718192021222324252627282930
class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; this.balance = balance; monthlyInterest(); } // This method might be used in other places too private void monthlyInterest() { if(accountType == "Savings") { // Plus 1 Percent balance += balance * 0.01; } else if(accountType == "HighYieldSavings") { // Plus 5 percent balance += balance * 0.05; } } }

Inicializar atributos importantes: es necesario inicializar todos los atributos importantes de un objeto con valores correctos para asegurar su funcionamiento adecuado, incluso si se trata de un constructor sin argumentos.

Mala práctica:

index.cs

index.cs

copy
123456789101112131415
public class Car { private string brand; private string model; private int year; private double price; // Constructor does not initialize important attributes // It is also generally not a good idea to have constructors without any arguments if they're not needed. public Car() { // No initialization of attributes Console.WriteLine("Car Created"); } }

Buena práctica:

index.cs

index.cs

copy
123456789101112131415161718192021222324252627282930313233343536373839
public class Car { private string brand; private string model; private int year; private double price; // Good: Constructor initializes important attributes // It also checks if the values are correct // In this case the if-else statements are not unnecessary since they are important for ensuring that the object functions correctly. public Car(string brand, string model, int year, double price) { this.brand = brand; this.model = model; // Validate and set the year // The first public car was created in 1886 :) if (year > 1886) { this.year = year; } else { Console.WriteLine("Invalid year. Setting year to default."); this.year = DateTime.Now.Year; // Set to current year as default } // Validate and set the price if (price >= 0) { this.price = price; } else { Console.WriteLine("Invalid price. Setting price to default."); this.price = 0; // Set to a default value } } }

1. ¿Qué característica nos permite crear múltiples constructores para una clase?

2. Es posible que necesite utilizar uno de los conceptos de las secciones anteriores en este cuestionario. El siguiente código tiene un error en las líneas 15 y 16. Revise el código detenidamente y decida cuál es una solución eficiente para este error:

question mark

¿Qué característica nos permite crear múltiples constructores para una clase?

Select the correct answer

question mark

Es posible que necesite utilizar uno de los conceptos de las secciones anteriores en este cuestionario. El siguiente código tiene un error en las líneas 15 y 16. Revise el código detenidamente y decida cuál es una solución eficiente para este error:

Select the correct answer

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 5. Capítulo 2

Pregunte a AI

expand

Pregunte a AI

ChatGPT

Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla

Awesome!

Completion rate improved to 2.04

bookHerencia

Desliza para mostrar el menú

En la sección anterior, revisaste el concepto de clases derivadas. Esta característica de una clase para heredar propiedades de otra clase se denomina Herencia.

Aunque ya conoces el concepto de Herencia, en esta ocasión lo abordarás de una manera un poco más completa para comprenderlo en mayor profundidad.

Como repaso rápido, a continuación se muestra un ejemplo de Herencia:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344
#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kilogram (1 kg = 2.2 pounds) } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Cat myCat = new Cat(); Dog myDog = new Dog(); myCat.speak(); myDog.speak(); } }
Note
Nota

La directiva #pragma warning disable "warning code" puede utilizarse para deshabilitar ciertas advertencias del compilador. Generalmente se desaconseja deshabilitar advertencias, ya que ignorarlas puede provocar comportamientos inesperados en el programa.

El código anterior contiene una clase padre llamada Mammal y dos clases derivadas llamadas Cat y Dog.

Observa que ninguna de las clases tiene un constructor definido explícitamente, lo que significa que estas clases utilizarán un constructor predeterminado cuando se cree un objeto.

Note
Nota

Un constructor predeterminado es proporcionado automáticamente por el lenguaje de programación si una clase no tiene ningún constructor definido explícitamente. Un constructor predeterminado es básicamente un constructor vacío que no contiene ningún código, por ejemplo public className() {} es cómo podría verse un constructor predeterminado. Dado que no inicializa ningún atributo explícitamente, todos los atributos toman valores predeterminados - también llamados valores cero.

Vamos a crear manualmente un constructor para la clase Mammal, que inicializa un objeto Mammal con algunos datos:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }

Si intentas compilar este programa, aparecerán algunos errores en la consola. Para comprender esos errores, primero necesitas entender dos conceptos importantes relacionados con los constructores.

El primero es que, una vez que defines explícitamente un constructor para una clase, esa clase ya no tiene un constructor predeterminado, por lo que el constructor definido explícitamente se convierte en el constructor principal de esa clase, que en este caso es:

index.cs

index.cs

copy
12345
public Mammal(int age, float weight) { this.age = age; this.weight = weight; }

Por lo tanto, al crear un nuevo objeto, siempre se deben pasar los argumentos requeridos del constructor en el orden correcto:

index.cs

index.cs

copy
1234567
// Incorrect ways to create 'Mammal', will show an error Mammal m1 = new Mammal(); Mammal m1 = new Mammal(10); Mammal m1 = new Mammal(42.0f); // Correct way to create 'Mammal', will execute fine. Mammal m1 = new Mammal(10, 42.0f);

En segundo lugar, las clases derivadas también pueden tener constructores; sin embargo, antes de que se llame al constructor de una clase derivada, también se llama al constructor de la clase base (padre):

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132333435
#pragma warning disable CS0169 // To disable some unnecessary warnings, using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg // No attribute is initialized explicitly in this constructor // Hence, all attributes will take up "zero" values // It is similar to a "default" constructor except it outputs a message public Mammal() { Console.WriteLine("Mammal Constructor Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog Constructor Called"); } } class ConsoleApp { static void Main() { Dog myDog = new Dog(); } }

Al ejecutar este código, se observa que el método WriteLine() del constructor de 'Mammal', que es la clase padre, se llama automáticamente. Esto significa que existe una regla según la cual el constructor de la clase base (también llamado constructor base) siempre se invoca antes que el constructor de la clase derivada.

Esta regla también se aplica en el caso de la herencia multinivel:

En el diagrama anterior, el constructor de Kitten llama al constructor de Cat antes que al suyo propio, sin embargo, dado que Cat también es una clase derivada, llama al constructor de Mammal antes que al suyo, y Mammal llama al constructor de Animal antes que al suyo, por lo tanto, en general, el primer constructor que se ejecuta es el constructor de la superclase — que es el constructor de la clase Animal y luego continúa hacia abajo desde allí.

Si el constructor de la clase padre no recibe ningún argumento, el compilador lo llama automáticamente, esta es la razón por la que el constructor de 'Mammal' en el ejemplo anterior fue llamado automáticamente. Sin embargo, veamos nuevamente el código defectuoso:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243
using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }

En el código anterior se obtienen dos errores que básicamente significan que no se han llamado manualmente los constructores base — ya que requieren algunos argumentos, es necesario llamarlos manualmente. La sintaxis básica para llamar manualmente al constructor de la clase padre es la siguiente:

index.cs

index.cs

copy
12345678910
class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }

Ejemplo:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132
using System; class ExampleParentClass { int value1; int value2; public ExampleParentClass(int value1, int value2) { this.value1 = value1; } } class ExampleDerivedClass : ExampleParentClass { int value3; // The value1 and value2 arguments are passed to the base class's contructor public ExampleDerivedClass(int value1, int value2, int value3) : base (value1, value2) { this.value3 = value3; } } class ConsoleApp { static void Main() { var testObject = new ExampleDerivedClass(5, 7, 9); } }

Usando esta sintaxis, puedes pasar todos los datos requeridos al constructor de Mammal a través de los constructores de Cat y Dog para corregir el error que obtenías antes:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public Cat(int age, float weight, string furPattern) : base(age, weight) { this.furPattern = furPattern; } public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); // Creating a "Dog" object with some data Dog d1 = new Dog(10, 42.5f, "Dobermann"); Console.WriteLine("Executed Successfully"); } }

Otra característica importante de los constructores es que puedes sobrecargar constructores de la misma manera que sobrecargas cualquier otro método. Puedes crear múltiples constructores con diferente número de argumentos:

index.cs

index.cs

copy
12345678910111213141516171819202122232425
class Mammal { int age; float weight; // kg // 1st constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor } // 2nd constructor public Mammal(int age) { this.age = age; } // 3rd constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; } }

En este caso, la clase Mammal tiene 3 constructores. Por lo tanto, es posible inicializar o crear un objeto mamífero de 3 maneras diferentes y el compilador elegirá qué constructor llamar según la cantidad y el tipo de argumentos:

index.cs

index.cs

copy
1234
// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);

Esto también significa que puedes llamar a cualquiera de los 3 constructores desde los constructores de la clase derivada. Por ejemplo, todos estos son válidos:

index.cs

index.cs

copy
123456789101112131415161718
// Using 3rd base constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } // Using 2nd base constructor public Dog(int age, string breed) : base(age) { this.breed = breed; } // Using 1st base constructor // If the base constructor has no arguments then it is automatically called (similar to the default constructor), so we don't necessarily need to write 'base()' public Dog(string breed) { this.breed = breed; }

Vamos a unir los dos fragmentos anteriores y agregar algunas sentencias Console.WriteLine para ver en qué orden se ejecutan los constructores y observar los resultados de manera práctica:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
using System; class Mammal { int age; float weight; // kg // 1st Constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor // The attributes are initialized with zero values Console.WriteLine("Mammal - Constructor 1 Called"); } // 2nd Constructor public Mammal(int age) { this.age = age; Console.WriteLine("Mammal - Constructor 2 Called"); } // 3rd Constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; Console.WriteLine("Mammal - Constructor 3 Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog - Constructor 1 Called"); } // Using 1st Mammal constructor // We don't necessarily need to write 'base()' in this case // It automatically finds and calls the constructor with no arguments public Dog(string breed) { this.breed = breed; Console.WriteLine("Dog - Constructor 2 Called"); } // Using 2nd Mammal constructor public Dog(int age, string breed) : base(age) { this.breed = breed; Console.WriteLine("Dog - Constructor 3 Called"); } // Using 3rd Mammal constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; Console.WriteLine("Dog - Constructor 4 Called"); } public void speak() { Console.WriteLine("Woof!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object using different constructors Mammal m1 = new Mammal(10, 42.0f); Mammal m2 = new Mammal(10); Mammal m3 = new Mammal(); Console.WriteLine("----------"); // Seperator, for ease of reading output // Creating a "Dog" object using different constructors Dog d1 = new Dog(10, 42.0f, "Dobermann"); Console.WriteLine(""); Dog d2 = new Dog(10, "Dobermann"); Console.WriteLine(""); Dog d3 = new Dog("Dobermann"); Console.WriteLine(""); Dog d4 = new Dog(); } }

Ahora que conoces las diferentes características de la herencia, también debes saber cómo y cuándo utilizarlas correctamente. A continuación, se presentan algunos aspectos a tener en cuenta al considerar una estructura de clases basada en herencia:

Equilibrio entre simplicidad y flexibilidad: La sobrecarga de constructores permite tener varios constructores que aceptan diferentes tipos de argumentos, pero abusar de esto puede hacer que el código sea más complejo y difícil de mantener. Es una buena práctica mantener el código de la clase breve, conciso y conveniente. Evita crear demasiados constructores para una clase para mantener un equilibrio entre simplicidad y flexibilidad.

Mantener los constructores simples: Los constructores deben ser responsables principalmente de inicializar un objeto con datos básicos. Es una buena práctica evitar procesamiento innecesario y lógica compleja dentro de un constructor. Si se necesita algún cálculo o lógica, es preferible crear un método separado para ello.

;Mala práctica:;

index.cs

index.cs

copy
123456789101112131415161718192021222324252627
class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; if (accountType == "Savings") { // Plus 1 Percent this.balance = balance + balance * 0.01; } else if (accountType == "HighYieldSavings") { // Plus 5 percent this.balance = balance + balance * 0.05; } else { this.balance = balance; } } }

Buena práctica:

index.cs

index.cs

copy
123456789101112131415161718192021222324252627282930
class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; this.balance = balance; monthlyInterest(); } // This method might be used in other places too private void monthlyInterest() { if(accountType == "Savings") { // Plus 1 Percent balance += balance * 0.01; } else if(accountType == "HighYieldSavings") { // Plus 5 percent balance += balance * 0.05; } } }

Inicializar atributos importantes: es necesario inicializar todos los atributos importantes de un objeto con valores correctos para asegurar su funcionamiento adecuado, incluso si se trata de un constructor sin argumentos.

Mala práctica:

index.cs

index.cs

copy
123456789101112131415
public class Car { private string brand; private string model; private int year; private double price; // Constructor does not initialize important attributes // It is also generally not a good idea to have constructors without any arguments if they're not needed. public Car() { // No initialization of attributes Console.WriteLine("Car Created"); } }

Buena práctica:

index.cs

index.cs

copy
123456789101112131415161718192021222324252627282930313233343536373839
public class Car { private string brand; private string model; private int year; private double price; // Good: Constructor initializes important attributes // It also checks if the values are correct // In this case the if-else statements are not unnecessary since they are important for ensuring that the object functions correctly. public Car(string brand, string model, int year, double price) { this.brand = brand; this.model = model; // Validate and set the year // The first public car was created in 1886 :) if (year > 1886) { this.year = year; } else { Console.WriteLine("Invalid year. Setting year to default."); this.year = DateTime.Now.Year; // Set to current year as default } // Validate and set the price if (price >= 0) { this.price = price; } else { Console.WriteLine("Invalid price. Setting price to default."); this.price = 0; // Set to a default value } } }

1. ¿Qué característica nos permite crear múltiples constructores para una clase?

2. Es posible que necesite utilizar uno de los conceptos de las secciones anteriores en este cuestionario. El siguiente código tiene un error en las líneas 15 y 16. Revise el código detenidamente y decida cuál es una solución eficiente para este error:

question mark

¿Qué característica nos permite crear múltiples constructores para una clase?

Select the correct answer

question mark

Es posible que necesite utilizar uno de los conceptos de las secciones anteriores en este cuestionario. El siguiente código tiene un error en las líneas 15 y 16. Revise el código detenidamente y decida cuál es una solución eficiente para este error:

Select the correct answer

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 5. Capítulo 2
some-alt