Indizes und BereicheIndices and ranges

Bereiche und Indizes bieten eine prägnante Syntax für den Zugriff auf einzelne Elemente oder Bereiche in einer Sequenz.Ranges and indices provide a succinct syntax for accessing single elements or ranges in a sequence.

In diesem Tutorial lernen Sie, wie die folgenden Aufgaben ausgeführt werden:In this tutorial, you'll learn how to:

  • Verwenden Sie die Syntax für Bereiche in einer Sequenz.Use the syntax for ranges in a sequence.
  • Lernen Sie die Entwurfsentscheidungen für Start und Ende jeder Sequenz kennen.Understand the design decisions for the start and end of each sequence.
  • Lernen Sie Szenarien für die Typen Index und Range kennen.Learn scenarios for the Index and Range types.

Sprachunterstützung für Indizes und BereicheLanguage support for indices and ranges

Diese Sprachunterstützung basiert auf zwei neuen Typen und zwei neuen Operatoren:This language support relies on two new types and two new operators:

  • System.Index: Stellt einen Index in einer Sequenz dar.System.Index represents an index into a sequence.
  • Der Index vom Endeoperator ^, der angibt, dass ein Index relativ zum Ende einer Sequenz ist.The index from end operator ^, which specifies that an index is relative to the end of a sequence.
  • System.Range: Stellt einen Unterbereich einer Sequenz dar.System.Range represents a sub range of a sequence.
  • Der Bereichsoperator .., der den Beginn und das Ende eines Bereichs als seine Operanden angibt.The range operator .., which specifies the start and end of a range as its operands.

Beginnen wir mit den Regeln für Indizes.Let's start with the rules for indices. Betrachten Sie einen Array sequence.Consider an array sequence. Der 0-Index entspricht sequence[0].The 0 index is the same as sequence[0]. Der ^0-Index entspricht sequence[sequence.Length].The ^0 index is the same as sequence[sequence.Length]. Der Ausdruck sequence[^0] löst eine Ausnahme aus, genau wie sequence[sequence.Length].The expression sequence[^0] does throw an exception, just as sequence[sequence.Length] does. Für eine beliebige Zahl n ist der Index ^n identisch mit 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

Sie können das letzte Wort mit dem ^1-Index abrufen.You can retrieve the last word with the ^1 index. Fügen Sie unter der Initialisierung folgenden Code hinzu:Add the following code below the initialization:

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

Ein Bereich gibt den Beginn und das Ende eines Bereichs an.A range specifies the start and end of a range. Bereiche sind exklusiv, d. h. das Ende ist nicht im Bereich enthalten.Ranges are exclusive, meaning the end isn't included in the range. Der Bereich [0..^0] stellt ebenso wie [0..sequence.Length] den gesamten Bereich dar.The range [0..^0] represents the entire range, just as [0..sequence.Length] represents the entire range.

Der folgende Code erzeugt einen Teilbereich mit den Worten „quick“, „brown“ und „fox“.The following code creates a subrange with the words "quick", "brown", and "fox". Er enthält words[1] bis words[3].It includes words[1] through words[3]. Das Element words[4] befindet sich nicht im Bereich.The element words[4] isn't in the range. Fügen Sie derselben Methode den folgenden Code hinzu.Add the following code to the same method. Kopieren Sie ihn, und fügen Sie ihn unten in das interaktive Fenster ein.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();

Der folgende Code erzeugt einen Teilbereich mit „lazy“ und „dog“.The following code creates a subrange with "lazy" and "dog". Dazu gehören words[^2] und words[^1].It includes words[^2] and words[^1]. Der Endindex words[^0] ist nicht enthalten.The end index words[^0] isn't included. Fügen Sie den folgenden Code auch hinzu:Add the following code as well:

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

Die folgenden Beispiele erstellen Bereiche, die am Anfang, am Ende und auf beiden Seiten offen sind: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();

Sie können Bereiche oder Indizes auch als Variablen deklarieren.You can also declare ranges or indices as variables. Die Variable kann dann innerhalb der Zeichen [ und ] verwendet werden: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();

Das folgende Beispiel zeigt viele der Gründe für diese Auswahl.The following sample shows many of the reasons for those choices. Ändern Sie x, y und z, um verschiedene Kombinationen zu testen.Modify x, y, and z to try different combinations. Verwenden Sie beim Experimentieren Werte, wo x kleiner ist als y und y kleiner als z für gültige Kombinationen.When you experiment, use values where x is less than y, and y is less than z for valid combinations. Fügen Sie den folgenden Code in einer neuen Methode hinzu.Add the following code in a new method. Probieren Sie verschiedene Kombinationen aus: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]}");

Typunterstützung für Indizes und BereicheType support for indices and ranges

Indizes und Bereiche stellen eine klare, präzise Syntax für den Zugriff auf ein einzelnes Element oder einen Teilbereich von Elementen in einer Sequenz bereit.Indexes and ranges provide clear, concise syntax to access a single element or a subrange of elements in a sequence. Ein Indexausdruck gibt in der Regel den Typ der Elemente einer Sequenz zurück.An index expression typically returns the type of the elements of a sequence. Ein Bereichsausdruck gibt in der Regel den gleichen Sequenztyp wie die Quellsequenz zurück.A range expression typically returns the same sequence type as the source sequence.

Jeder Typ, der einen Indexer mit einem Index- oder Range-Parameter bereitstellt, unterstützt explizit Indizes bzw. Bereiche.Any type that provides an indexer with an Index or Range parameter explicitly supports indices or ranges respectively. Ein Indexer, der einen einzelnen Range-Parameter annimmt, kann einen anderen Sequenztyp zurückgeben, z. B. System.Span<T>.An indexer that takes a single Range parameter may return a different sequence type, such as System.Span<T>.

Ein Typ ist zählbar, wenn er über eine Eigenschaft mit dem Namen Length oder Count mit einem zugreifbaren Getter und einem Rückgabetyp von int verfügt.A type is countable if it has a property named Length or Count with an accessible getter and a return type of int. Ein zählbarer Typ, der Indizes oder Bereiche nicht explizit unterstützt, kann implizite Unterstützung dafür bieten.A countable type that doesn't explicitly support indices or ranges may provide an implicit support for them. Weitere Informationen finden Sie in den Abschnitten Implizite Indexunterstützung und Implizite Bereichsunterstützung der Featurevorschläge.For more information, see the Implicit Index support and Implicit Range support sections of the feature proposal note. Bereiche, die die implizite Bereichsunterstützung verwenden, geben denselben Sequenztyp wie die Quellsequenz zurück.Ranges using implicit range support return the same sequence type as the source sequence.

Beispielsweise unterstützen die folgenden .NET-Typen Indizes und Bereiche: String, Span<T> und ReadOnlySpan<T>.For example, the following .NET types support both indices and ranges: String, Span<T>, and ReadOnlySpan<T>. List<T> unterstützt Indizes, jedoch keine Bereiche.The List<T> supports indices but doesn't support ranges.

Array zeigt ein differenzierteres Verhalten.Array has more nuanced behavior. Eindimensionale Arrays unterstützen sowohl Indizes als auch Bereiche.Single dimension arrays support both indices and ranges. Bei mehrdimensionalen Arrays ist dies nicht der Fall.Multi-dimensional arrays don't. Der Indexer für ein mehrdimensionales Array verfügt über mehrere Parameter, nicht über einen einzelnen Parameter.The indexer for a multi-dimensional array has multiple parameters, not a single parameter. Jagged Arrays, auch als Array von Arrays bezeichnet, unterstützen sowohl Bereiche als auch Indexer.Jagged arrays, also referred to as an array of arrays, support both ranges and indexers. Das folgende Beispiel zeigt, wie ein rechteckiger Unterabschnitt eines Jagged Arrays durchlaufen wird.The following example shows how to iterate a rectangular subsection of a jagged array. Es durchläuft den Abschnitt in der Mitte, wobei die ersten und letzten drei Zeilen sowie die ersten und letzten zwei Spalten jeder ausgewählten Zeile ausgeschlossen werden:It iterates the section in the center, excluding the first and last three rows, and the first and last two columns from each selected row:

var jagged = new int[10][]
{
   new int[10] { 0,  1, 2, 3, 4, 5, 6, 7, 8, 9},
   new int[10] { 10,11,12,13,14,15,16,17,18,19},
   new int[10] { 20,21,22,23,24,25,26,27,28,29},
   new int[10] { 30,31,32,33,34,35,36,37,38,39},
   new int[10] { 40,41,42,43,44,45,46,47,48,49},
   new int[10] { 50,51,52,53,54,55,56,57,58,59},
   new int[10] { 60,61,62,63,64,65,66,67,68,69},
   new int[10] { 70,71,72,73,74,75,76,77,78,79},
   new int[10] { 80,81,82,83,84,85,86,87,88,89},
   new int[10] { 90,91,92,93,94,95,96,97,98,99},
};

var selectedRows = jagged[3..^3];

foreach (var row in selectedRows)
{
    var selectedColumns = row[2..^2];
    foreach (var cell in selectedColumns)
    {
        Console.Write($"{cell}, ");
    }
    Console.WriteLine();
}

Szenarien für Indizes und BereicheScenarios for indices and ranges

Sie werden oft Bereiche und Indizes verwenden, wenn Sie einen Teilbereich einer größeren Sequenz analysieren möchten.You'll often use ranges and indices when you want to analyze a subrange of a larger sequence. Aus der neuen Syntax lässt sich klarer herauslesen, welcher Teilbereich beteiligt ist.The new syntax is clearer in reading exactly what subrange is involved. Die lokale Funktion MovingAverage nimmt einen Range als Argument entgegen.The local function MovingAverage takes a Range as its argument. Die Methode listet dann genau diesen Bereich bei der Berechnung von Minimum, Maximum und Durchschnitt auf.The method then enumerates just that range when calculating the min, max, and average. Probieren Sie den folgenden Code in Ihrem Projekt aus: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();