DEV Community

Baltasar García Perez-Schofield
Baltasar García Perez-Schofield

Posted on • Edited on

C#: IDE, por favor, no me "ayudes" más... (II)

Otro caso en el que los subrayados rojos aparecen demasiado pronto, es el siguiente. Recuerda la clase Calificacion, que guardaba una referencia a una asignatura y una nota. Necesitamos la clase CalificacionTeoriaPractica, que acepta también una referencia a una asignatura y, en este caso, dos notas, una de teoría y otra de prácticas.

Así que creamos la clase CalificacionTeoriaPractica hereda de la clase Calificacion. Empezamos a teclear y...

Empezando con la clase **Calificacion**

Pues nada... ya nos aparece subrayada en rojo... en fin... hacemos de tripas corazón y continuamos para escribir el constructor. Y también nos lo subraya en rojo.

Y el constructor de **Calificacion**

Hemos "aguantado" hasta ahora, pero llega el momento de poner el cursor del ratón encima de la parte subrayada y ver qué tripa se le ha roto... quiero decir, qué error tiene nuestro código.

El problema es que un estudiante habría entrado ya en modo frenético quita-el-subrayado-o-muere, y se encuentra con que se le ofrecen dos posibilidades:

Context actions
Add 'base' initializer
Add default constructor in class 'Calificacion'
...

Que podemos resumir en cuanto a significado como sigue:

Mecanismo Significado
Inicializador 'base' Ejecuta el constructor de la clase base con los parámetros dados
Constructor por defecto Constructor sin parámetros

La primera añade el texto : base() a nuestra declaración, de forma que tendríamos:

class CalificacionTeoriaPracticas: Calificacion {
    public CalificacionTeoriaPracticas(Asignatura asignatura, decimal teoria, decimal practica): base()
    {

    }
}
Enter fullscreen mode Exit fullscreen mode

Esto no soluciona el problema, sino que ahora el subrayado se ha movido a la última parte (ese base()), que el IDE nos ha añadido. Si lo que queremos es eliminar el subrayado rojo, no hemos mejorado gran cosa. De acuerdo, escojamos la segunda opción. Y... ¡bingo! ¡Todos los subrayados rojos han desaparecido! ¡Esta claro que esta es la opción correcta!

El problema es que no, no es la opción correcta. De hecho, dudo que sea la opción correcta en el 99% de los casos, lo que me hace preguntarme por qué se creo esa posibilidad. Quizás porque era "fácil" de añadir... no lo sé.

De acuerdo, continuamos con el programa:

class CalificacionTeoriaPracticas: Calificacion {
    public CalificacionTeoriaPracticas(Asignatura asignatura, decimal teoria, decimal practica)
    {
        this.Asignatura = asignatura;
        this.Nota = ( teoria + practica ) / 2;
    }
}
Enter fullscreen mode Exit fullscreen mode

¡El problema ha vuelto! ¡El contenido del constructor, las dos líneas están subrayadas en rojo!

De acuerdo, el problema es que las notas están marcadas como de solo lectura. Puedo modificarlas para que puedan ser escritas desde clases derivadas, con protect.

public class Calificacion: IComparable<Calificacion> {
    // más cosas...

    public decimal Nota { get; protected set; }
    public Asignatura Asignatura { get; protected set; }
}
Enter fullscreen mode Exit fullscreen mode

Ufff... hecho. Y no he tenido que crear las propiedades como de escritura pública. Bien. Utilicemos el nuevo código.

var asig = new Asignatura( "Matracas" );
var nota = new Calificacion( asig, 7.8m );
var nota2 = new CalificacionTeoriaPracticas( asig, 7m, 6m );

Console.WriteLine( $"{nota.Asignatura.Nombre}: {nota.Nota}" );
Console.WriteLine( $"{nota2.Asignatura.Nombre}: {nota2.Nota}" );
Enter fullscreen mode Exit fullscreen mode

Así que compilo, ejecuto, y... sucede algo raro.

Unhandled exception. System.NotImplementedException: The method or operation is not implemented.
Enter fullscreen mode Exit fullscreen mode

¡¿Pero qué?! ¿El método no está implementado? Veamos el código de CalificacionTeoriaPractica... nada raro... ¿y el de Calificacion? Bueno, apenas está cambiado, pero... por si acaso.

public class Calificacion {
    public Calificacion(Asignatura asigantura, decimal nota)
    {
        this.Asignatura = asigantura;
        this.Nota = nota;
    }

    protected Calificacion()
    {
        throw new NotImplementedException();
    }

    // ...
}
Enter fullscreen mode Exit fullscreen mode

¿Se refiería a esto el IDE cuando decía que iba a añadir un constructor por defecto? A ver, es protected, pero... de todas formas, ¿qué puedo poner ahí? ¿Nada?

public class Calificacion {
    public Calificacion(Asignatura asigantura, decimal nota)
    {
        this.Asignatura = asigantura;
        this.Nota = nota;
    }

    protected Calificacion()
    {
    }

    // ...
}
Enter fullscreen mode Exit fullscreen mode

Veamos, el problema ha desaparecido. Una compilación y ejecución más tarde...

Matracas: 7,8
Matracas: 6,5
Enter fullscreen mode Exit fullscreen mode

¡Bien, funciona!

Pero... ¿realmente hemos arreglado algo? Repasemos: hemos modificado las propiedades de la clase base, hemos creado un constructor por defecto (vacío, es decir, que no sirve para nada), solo porque hemos escogido una opción de las ayudas de del IDE.

Ahora hemos llegado a un punto en el que los objetos Calificacion pueden crearse de dos maneras: la primera, siguiendo el constructor parametrizado en Calificacion. Pero para los objetos de la clase derivada de Calificacion, CalificacionTeoriaPractica, la construcción no implica el constructor anterior, sino el constructor parametrizado de CalificacionTeoriaPractica (donde se inicializan las propiedades directamente), y el constructor por defecto (donde, en realidad, no se hace nada).

Sin querer, hemos violado el principio DRY (No te repitas, o Don't repeat yourself). En el momento en el que la clase Calificacion cambie su manera de construirse, tendremos que tener en cuenta un constructor para los objetos propios, y el constructor por defecto para los objetos de las clases derivadas.

Siguiendo la intuición de que tiene que existir un mundo mejor, ahí fuera, en alguna parte, deshagamos lo hecho y probemos de nuevo, tratando de olvidarnos de los subrayados.

Devolvamos la clase Calificacion a su estado anterior.

public class Calificacion: IComparable<Calificacion> {
    public Calificacion(Asignatura asigantura, decimal nota)
    {
        this.Asignatura = asigantura;
        this.Nota = nota;
    }

    // más cosas...

    public decimal Nota { get; }
    public Asignatura Asignatura { get; }
}
Enter fullscreen mode Exit fullscreen mode

Y si volvemos al punto de partida, la clase CalificacionTeoriaPractica:

class CalificacionTeoriaPracticas: Calificacion {
    public CalificacionTeoriaPracticas(Asignatura asignatura, decimal teoria, decimal practica)
    {
    }
}
Enter fullscreen mode Exit fullscreen mode

Podemos, simplemente, llamar al método de la clase base utilizando el inicializador base, de la forma más sencilla, sin necesidad de modificar la clase base en absoluto.

class CalificacionTeoriaPracticas: Calificacion {
    public CalificacionTeoriaPracticas(Asignatura asignatura, decimal teoria, decimal practica)
    :base( asignatura, ( teoria + practica ) / 2 )
    {
    }
}
Enter fullscreen mode Exit fullscreen mode

...y ya está. Era tan sencillo como esto.

Ahora imaginémonos que esto ocurre en un proyecto real, en lugar de un escenario imaginario buscando la máxima simplicidad...

Continua...

Top comments (0)