Índices y rangosIndices and ranges

Los intervalos e índices proporcionan una sintaxis concisa para acceder a elementos únicos o intervalos en una secuencia.Ranges and indices provide a succinct syntax for accessing single elements or ranges in a sequence.

En este tutorial aprenderá lo siguiente:In this tutorial, you'll learn how to:

  • Usar la sintaxis para intervalos de una secuencia.Use the syntax for ranges in a sequence.
  • Comprender las decisiones de diseño para iniciar y finalizar cada secuencia.Understand the design decisions for the start and end of each sequence.
  • Descubrir escenarios para los tipos Index y Range.Learn scenarios for the Index and Range types.

Compatibilidad con idiomas para los índices y los rangosLanguage support for indices and ranges

Esta compatibilidad con lenguajes se basa en dos nuevos tipos y dos nuevos operadores:This language support relies on two new types and two new operators:

  • System.Index representa un índice en una secuencia.System.Index represents an index into a sequence.
  • Índice desde el operador final ^, que especifica que un índice es relativo al final de una secuencia.The index from end operator ^, which specifies that an index is relative to the end of a sequence.
  • System.Range representa un subrango de una secuencia.System.Range represents a sub range of a sequence.
  • El operador de intervalo .., que especifica el inicio y el final de un intervalo como sus operandos.The range operator .., which specifies the start and end of a range as its operands.

Comencemos con las reglas de los índices.Let's start with the rules for indices. Considere un elemento sequence de matriz.Consider an array sequence. El índice 0 es igual que sequence[0].The 0 index is the same as sequence[0]. El índice ^0 es igual que sequence[sequence.Length].The ^0 index is the same as sequence[sequence.Length]. Tenga en cuenta que sequence[^0] produce una excepción, al igual que sequence[sequence.Length].Note that sequence[^0] does throw an exception, just as sequence[sequence.Length] does. Para cualquier número n, el índice ^n es igual que sequence[sequence.Length - n].For any number n, the index ^n is the same as sequence[sequence.Length - n].

string[] words = new string[]
{
                // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

Puede recuperar la última palabra con el índice ^1.You can retrieve the last word with the ^1 index. Agregue el código siguiente a la inicialización:Add the following code below the initialization:

Console.WriteLine($"The last word is {words[^1]}");

Un rango especifica el inicio y el final de un intervalo.A range specifies the start and end of a range. Los rangos son excluyentes, lo que significa que el final no se incluye en el intervalo.Ranges are exclusive, meaning the end is not included in the range. El rango [0..^0] representa todo el intervalo, al igual que [0..sequence.Length] representa todo el intervalo.The range [0..^0] represents the entire range, just as [0..sequence.Length] represents the entire range.

El siguiente código crea un subrango con las palabras "quick", "brown" y "fox".The following code creates a subrange with the words "quick", "brown", and "fox". Va de words[1] a words[3].It includes words[1] through words[3]. El elemento words[4] no se encuentra en el intervalo.The element words[4] isn't in the range. Agregue el código siguiente al mismo método.Add the following code to the same method. Cópielo y péguelo en la parte inferior de la ventana interactiva.Copy and paste it at the bottom of the interactive window.

string[] quickBrownFox = words[1..4];
foreach (var word in quickBrownFox)
    Console.Write($"< {word} >");
Console.WriteLine();

El siguiente código crea un subrango con "lazy" y "dog".The following code creates a subrange with "lazy" and "dog". Incluye words[^2] y words[^1].It includes words[^2] and words[^1]. El índice del final words[^0] no se incluye.The end index words[^0] isn't included. Agregue el código siguiente también:Add the following code as well:

string[] lazyDog = words[^2..^0];
foreach (var word in lazyDog)
    Console.Write($"< {word} >");
Console.WriteLine();

En los ejemplos siguientes se crean rangos con final abierto para el inicio, el final o ambos:The following examples create ranges that are open ended for the start, end, or both:

string[] allWords = words[..]; // contains "The" through "dog".
string[] firstPhrase = words[..4]; // contains "The" through "fox"
string[] lastPhrase = words[6..]; // contains "the, "lazy" and "dog"
foreach (var word in allWords)
    Console.Write($"< {word} >");
Console.WriteLine();
foreach (var word in firstPhrase)
    Console.Write($"< {word} >");
Console.WriteLine();
foreach (var word in lastPhrase)
    Console.Write($"< {word} >");
Console.WriteLine();

También puede declarar rangos o índices como variables.You can also declare ranges or indices as variables. La variable se puede usar luego dentro de los caracteres [ y ]:The variable can then be used inside the [ and ] characters:

Index the = ^3;
Console.WriteLine(words[the]);
Range phrase = 1..4;
string[] text = words[phrase];
foreach (var word in text)
    Console.Write($"< {word} >");
Console.WriteLine();

El ejemplo siguiente muestra muchos de los motivos para esas opciones.The following sample shows many of the reasons for those choices. Modifique x, y y z para probar diferentes combinaciones.Modify x, y, and z to try different combinations. Al experimentar, use valores donde x sea menor que y y y sea menor que z para las combinaciones válidas.When you experiment, use values where x is less than y, and y is less than z for valid combinations. Agregue el código siguiente a un nuevo método.Add the following code in a new method. Pruebe diferentes combinaciones:Try different combinations:

int[] numbers = Enumerable.Range(0, 100).ToArray();
int x = 12;
int y = 25;
int z = 36;

Console.WriteLine($"{numbers[^x]} is the same as {numbers[numbers.Length - x]}");
Console.WriteLine($"{numbers[x..y].Length} is the same as {y - x}");

Console.WriteLine("numbers[x..y] and numbers[y..z] are consecutive and disjoint:");
Span<int> x_y = numbers[x..y];
Span<int> y_z = numbers[y..z];
Console.WriteLine($"\tnumbers[x..y] is {x_y[0]} through {x_y[^1]}, numbers[y..z] is {y_z[0]} through {y_z[^1]}");

Console.WriteLine("numbers[x..^x] removes x elements at each end:");
Span<int> x_x = numbers[x..^x];
Console.WriteLine($"\tnumbers[x..^x] starts with {x_x[0]} and ends with {x_x[^1]}");

Console.WriteLine("numbers[..x] means numbers[0..x] and numbers[x..] means numbers[x..^0]");
Span<int> start_x = numbers[..x];
Span<int> zero_x = numbers[0..x];
Console.WriteLine($"\t{start_x[0]}..{start_x[^1]} is the same as {zero_x[0]}..{zero_x[^1]}");
Span<int> z_end = numbers[z..];
Span<int> z_zero = numbers[z..^0];
Console.WriteLine($"\t{z_end[0]}..{z_end[^1]} is the same as {z_zero[0]}..{z_zero[^1]}");

Compatibilidad con tipos para los índices y los rangosType support for indices and ranges

Los índices y los intervalos proporcionan una sintaxis clara y concisa para acceder a un único elemento o a un subconjunto de elementos de una secuencia.Indexes and ranges provide clear, concise syntax to access a single element or a sub-range of elements in a sequence. Normalmente, una expresión de índice devuelve el tipo de los elementos de una secuencia.An index expression typically returns the type of the elements of a sequence. Una expresión de rango suele devolver el mismo tipo de secuencia que la secuencia de origen.A range expression typically returns the same sequence type as the source sequence.

Si un tipo proporciona un indizador con un parámetro Index o Range, admite de manera explícita los índices o rangos, respectivamente.If a type provides an indexer with an Index or Range parameter, it explicitly supports indices or ranges respectively. Cuando el tipo proporciona un indexador que toma un único parámetro de Range, puede optar por devolver un tipo de secuencia diferente, como System.Span<T>.When the type provides an indexer that takes a single Range parameter, it may choose to return a different sequence type, such as System.Span<T>.

Un tipo es contable si tiene una propiedad denominada Length o Count con un captador accesible y un tipo de valor devuelto de int.A type is countable if it has a property named Length or Count with an accessible getter and a return type of int. Un tipo contable que no admite índices ni rangos de manera explícita podría admitirlos implícitamente.A countable type that doesn't explicitly support indices or ranges may provide an implicit support for them. Para más información, consulte las secciones Compatibilidad implícita de índices y Compatibilidad implícita de rangos de la nota de propuesta de características.For more information, see the Implicit Index support and Implicit Range support sections of the feature proposal note. Los rangos que usan la compatibilidad implícita del rango devuelven el mismo tipo de secuencia que la secuencia de origen.Ranges using implicit range support return the same sequence type as the source sequence.

Por ejemplo, los tipos de .NET siguientes admiten tanto índices como rangos: Array, String, Span<T> y ReadOnlySpan<T>.For example, the following .NET types support both indices and ranges: Array, String, Span<T>, and ReadOnlySpan<T>. List<T> admite índices, pero no rangos.The List<T> supports indices but doesn't support ranges.

Escenarios para los índices y los rangosScenarios for indices and ranges

A menudo usará rangos e índices cuando desee realizar un poco de análisis en el subrango de una secuencia completa.You'll often use ranges and indices when you want to perform some analysis on subrange of an entire sequence. La nueva sintaxis es más clara al leer exactamente lo que implica el subrango.The new syntax is clearer in reading exactly what subrange is involved. La función local MovingAverage toma un Range como su argumento.The local function MovingAverage takes a Range as its argument. El método enumera solo ese rango al calcular el mínimo, el máximo y la media.The method then enumerates just that range when calculating the min, max, and average. Pruebe con el código siguiente en su proyecto:Try the following code in your project:

int[] sequence = Sequence(1000);

for(int start = 0; start < sequence.Length; start += 100)
{
    Range r = start..(start+10);
    var (min, max, average) = MovingAverage(sequence, r);
    Console.WriteLine($"From {r.Start} to {r.End}:    \tMin: {min},\tMax: {max},\tAverage: {average}");
}

for (int start = 0; start < sequence.Length; start += 100)
{
    Range r = ^(start + 10)..^start;
    var (min, max, average) = MovingAverage(sequence, r);
    Console.WriteLine($"From {r.Start} to {r.End}:  \tMin: {min},\tMax: {max},\tAverage: {average}");
}

(int min, int max, double average) MovingAverage(int[] subSequence, Range range) =>
    (
        subSequence[range].Min(),
        subSequence[range].Max(),
        subSequence[range].Average()
    );

int[] Sequence(int count) =>
    Enumerable.Range(0, count).Select(x => (int)(Math.Sqrt(x) * 100)).ToArray();

Puede descargar el código completado del repositorio dotnet/samples.You can download the completed code from the dotnet/samples repository.