C#: IDE, por favor, no me "ayudes" más... (II)
Tags: spanish, csharp, programming, discuss
Published: 2023-12-20 13:07:49

---
title: C#: IDE, por favor, no me "ayudes" más... (II)
published: true
series: ¿El IDE ayuda?
description: Cuando tu IDE te obliga a crear el constructor que en realidad no quieres...
tags: #spanish #csharp #programming #discuss
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-12-20 11:38 +0000
---

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**](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q1mxhask2hfnxx75ef0m.png)

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**](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l0lagds3vvg7dq9b1iya.png)

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:

```c#
class CalificacionTeoriaPracticas: Calificacion {
    public CalificacionTeoriaPracticas(Asignatura asignatura, decimal teoria, decimal practica): base()
    {
        
    }
}
```

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:

```c#
class CalificacionTeoriaPracticas: Calificacion {
    public CalificacionTeoriaPracticas(Asignatura asignatura, decimal teoria, decimal practica)
    {
        this.Asignatura = asignatura;
        this.Nota = ( teoria + practica ) / 2;
    }
}
```

¡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*.

```c#
public class Calificacion: IComparable<Calificacion> {
    // más cosas...
    
    public decimal Nota { get; protected set; }
    public Asignatura Asignatura { get; protected set; }
}
```

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

```c#
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}" );
```

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

    Unhandled exception. System.NotImplementedException: The method or operation is not implemented.

¡¿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.

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

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

    // ...
}
```

¿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?

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

    protected Calificacion()
    {
    }

    // ...
}
```

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

    Matracas: 7,8
    Matracas: 6,5

¡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.

```c#
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; }
}
```

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

```c#
class CalificacionTeoriaPracticas: Calificacion {
    public CalificacionTeoriaPracticas(Asignatura asignatura, decimal teoria, decimal practica)
    {
    }
}
```

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.

```c#
class CalificacionTeoriaPracticas: Calificacion {
    public CalificacionTeoriaPracticas(Asignatura asignatura, decimal teoria, decimal practica)
    :base( asignatura, ( teoria + practica ) / 2 )
    {
    }
}
```

...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...](https://dev.to/baltasarq/c-ide-por-favor-no-me-ayudes-mas-iii-k4d)