Recientemente estaba impartiendo clases y puse un ejemplo sobre una clase Poligono.
public class Poligon {
public Poligon()
{
this._points = new List<Point>( 3 );
}
public void Add(Point p)
{
this._points.Add( p );
}
public int Count => this._points.Count;
public Point this[int i] {
get {
if ( i < 0
|| i >= this.Count )
{
throw new IndexOutOfRangeException( "" + i );
}
return this._points[ i ];
}
}
public override string ToString() => String.Join( ", ", this._points );
private readonly IList<Point> _points;
}
En clases utilizo Rider, ya que es una solución multiplataforma, y resulta que... bueno, como ya todo en esta vida, y cuando digo todo, me refiero a todo, pues ahora viene con IA.
Esto se traduce en que, al pulsar ENTER y teclear el comienzo de una nueva sentencia, o incluso sin teclear nada, Rider te sugiere el código que probablemente vas a utilizar. Supongo que la idea es que 1) vayas más rápido programando, y 2) puedas confiar en código que no tiene errores.
¿No? Pues va a ser que no.
Resulta que, para insistir en el concepto de la necesidad de la encapsulación, decidí crear una propiedad Points, que devolvería una colección con todos los puntos del polígono. Así que escribo public IList<Point> ...
y lo que me sugiere la IA es:
public IList<Point> Points => this._points;
Venga ya.
Tengo que reconocer que fue una forma de reforzar aún más este tema. Reirse de la IA suele ser una buena forma de hacerlo: si vas a devolver una colección encapsulada dentro de una clase, entonces la clase no sirve para nada.
Otra forma de entenderlo es que si realmente pulsáramos el tabulador para aceptar este código, habríamos abierto una puerta de dos hojas al contenido de la clase, mientras que el objetivo central de cualquier clase es que solo se pueda acceder a lo que la interfaz (la colección de miembros públicos), permita.
O dicho de otra forma, a partir de este código:
var p0 = new Point{ X = 11, Y = 12 };
var p1 = new Point{ X = 21, Y = 42 };
var p2 = new Point{ X = 19, Y = 39 };
var p = new Poligon();
p.Add( p0 );
p.Add( p1 );
p.Add( p2 );
Console.WriteLine( p ); // (11, 12), (21, 42), (19, 39)
...podríamos llegar a esto:
var p0 = new Point{ X = 11, Y = 12 };
var p1 = new Point{ X = 21, Y = 42 };
var p2 = new Point{ X = 19, Y = 39 };
var p = new Poligon();
p.Add( p0 );
p.Add( p1 );
p.Add( p2 );
p.Points.Clear();
p.Points.Add( 111, 111 );
Console.WriteLine( p ); // (111, 111)
...código que no tiene sentido, dado que la única forma de modificar la colección que hemos diseñado solo acepta Add(p: Point)
como manera de modificar la colección.
La forma correcta de crear la propiedad Points es una de las siguientes:
public IList<Point> Points => this._points.AsReadOnly();
public IReadOnlyList<Point> Points => this._points.AsReadOnly();
public IList<Point> Points => new List<Point>( this._points
p.);
El primero devuelve lo que es aparentemente una lista normal, pero cualquier llamada a un método que la modifique (Add(), Clear()...), produce una excepción.
El segundo devuelve una lista de solo lectura, de forma que los métodos que la modifican han desaparecido.
La tercera es la más categórica: crea una nueva lista (es decir, duplica el contenido en memoria), de forma que cualquier modificación que se haga sobre ella... se perderá... como lágrimas en la lluvia.
Y si aceptas el erróneo código sugerido, o las dos versiones del código correcto que no devuelven una IReadOnlyList<...>
, entonces a la hora de añadir elementos, lo que nos sugiere al teclear p.
, tras crear los puntos p0
, p1
y p2
, es:
var p0 = new Point{ X = 11, Y = 12 };
var p1 = new Point{ X = 21, Y = 42 };
var p2 = new Point{ X = 19, Y = 39 };
var p = new Poligon();
p.Points.Add( p0 );
p.Points.Add( p1 );
p.Points.Add( p2 );
Console.WriteLine( p ); // nada
Pues lo dicho. Lágrimas en la lluvia.
Lo mejor es que, aunque desactives el plugin, sigue sugiriendo código. En fin...
Top comments (0)