Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

18 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 4 de junio de 2019
.NET Core Seguimos analizando las novedades que traerá C# 8, y esta vez vamos a detenernos en una característica que aportará algo más de agilidad a la hora de trocear o acceder a elementos de arrays y algunos tipos de colecciones similares, como Span<T>.

Como muchas otras características del lenguaje, se trata de algunos azucarillos sintácticos creados en torno a dos nuevos tipos añadidos a las bibliotecas básicas del framework: las estructuras System.Index y System.Range. Por esta razón, para utilizar estos elementos no sólo es necesario disponer de nuevos compiladores, sino también de nuevas versiones del framework.
Recordad que a día de hoy ya se puede probar C# 8 en Visual Studio 2019 o directamente desde la interfaz de línea de comandos de .NET Core.

Índices de arrays con System.Index

Hasta esta versión de C#, la forma natural de acceder a un elemento específico de un array era indicando el entero que representaba su posición en el mismo. Por ejemplo, con arr[0] podíamos acceder al primer elemento de la colección, o con arr[arr.Length-1] al último.

La nueva estructura Index proporciona una fórmula para almacenar y gestionar índices usando tipos específicamente diseñados para ello, lo que permite añadir algo más de flexibilidad al resultado. Su uso básico, que no parece demasiado útil, es el siguiente:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

Index i0 = new Index(0);
Index i1 = new Index(1);
Index i7 = new Index(7);

Console.WriteLine(primes[i0]); // 2
Console.WriteLine(primes[i1]); // 3
Console.WriteLine(primes[i7]); // 19
Así al vistazo no parece algo que valga demasiado la pena, ¿verdad? Bueno, pues como aportación interesante, a la hora de crear un índice podemos indicar si la referencia la hacemos desde el principio o desde el final de la secuencia:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

Index fromStart3 = new Index(3); // Por defecto, 'fromEnd' es falso
Index fromEnd1 = new Index(1, fromEnd: true);
Index fromEnd8 = new Index(8, fromEnd: true);

Console.WriteLine(primes[fromStart3]); // 7
Console.WriteLine(primes[fromEnd1]); // 19
Console.WriteLine(primes[fromEnd8]); // 2
¡Alto ahí! Fijaos en un detalle sumamente importante: cuando comenzamos por el final, el índice 1 es el primer elemento. O en otras palabras, un acceso desde el final al índice 0 daría un error, porque este elemento sería posterior al último item de la colección; sin embargo, cuando contamos desde el principio, el índice cero es el primero.
var ceroFromStart = new Index(0);
var ceroFromEnd = new Index(0, fromEnd: true);
Console.WriteLine(primes[ceroFromStart]); // 2
Console.WriteLine(primes[ceroFromEnd]); // Error: IndexOutOfRangeException
Por último, cabe añadir que la estructura Index dispone de algunos métodos estáticos para agilizar la creación de índices, como Index.FromStart() o Index.FromEnd(), o para obtener referencias al comienzo y final (recordad, ¡fuera del array!) con Index.Start e Index.End respectivamente:
Index fromEnd3A = Index.FromEnd(3);
Index fromEnd3B = new Index(3, fromEnd: true);
Console.WriteLine(fromEnd3A.Equals(fromEnd3A)); // True

var start = Index.Start;
var alsoStart = Index.FromStart(0);
Console.WriteLine(start.Equals(alsoStart)); // True

Conversiones implícitas y el operador hat ^

Anteriormente hemos visto lo fácil que es crear índices y utilizarlos para acceder a los elementos de una colección. Pero, obviamente, esto no podía quedar ahí.

En primer lugar, el tipo Index dispone de conversiones implícitas hacia y desde int, lo que quiere decir que normalmente podremos utilizar estos dos tipos indistintamente, como se muestra en el siguiente ejemplo:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };
Index fromStart3a = new Index(3);
Index fromStart3b = 3;
int fromStart3c = 3;

Console.WriteLine(primes[fromStart3a]); // 7
Console.WriteLine(primes[fromStart3b]); // 7
Console.WriteLine(primes[fromStart3c]); // 7
Console.WriteLine(fromStart3a.Equals(fromStart3c)); // True
Vale, esto simplifica la codificación de índices comenzando por el principio, igual que lo hemos hecho siempre con enteros, pero, ¿qué ocurre con la indexación desde el final?

Y aquí es donde entra en juego el nuevo uso del operador hat (sombrero) ^ de C# 8. Este operador es un mero azucarillo sintáctico para facilitar la creación de índices desde el final, como hacíamos con Index.FromEnd(), pero de forma más compacta. Veamos unos ejemplos:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };
var fromEnd2A = Index.FromEnd(2);
var fromEnd2B = ^2; // = Index.FromEnd(2)

Console.WriteLine(primes[fromEnd2A] == primes[fromEnd2B]); // True
Console.WriteLine(primes[fromEnd2B]); // 17
En el siguiente post veremos cómo la nueva estructura Range permite especificar rangos o extraer porciones de colecciones indicando los límites superiores e inferiores, y cómo se vale de Index para ello.

Publicado en: www.variablenotfound.com.

2 Comentarios:

Jose Fabricio Rojas Quiroz dijo...

Muy bueno, gracias por el update

José María Aguilar dijo...

Me alegra saber que te resulta interesante. Y muchas gracias por comentar :)

Saludos!