Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Rozsahy a indexy poskytují stručnou syntaxi pro přístup k jednoduchým prvkům nebo rozsahům v posloupnosti.
V tomto kurzu se naučíte:
Podpora jazyka pro indexy a rozsahy
Indexy a rozsahy poskytují stručnou syntaxi pro přístup k jednoduchým prvkům nebo rozsahům v posloupnosti.
Podpora tohoto jazyka spoléhá na dva nové typy a dva nové operátory:
- System.Index představuje index v sekvenci.
-
Index z koncového operátoru
^, který určuje, že index je relativní ke konci sekvence. - System.Range představuje podsestavu sekvence.
-
Operátor
..rozsahu, který určuje začátek a konec rozsahu jako operandy.
Začněme pravidly indexů. Zvažte pole sequence. Index 0 je stejný jako sequence[0]. Index ^0 je stejný jako sequence[sequence.Length]. Výraz sequence[^0] vyvolá výjimku, stejně jako sequence[sequence.Length] tomu je. U libovolného čísla nje index ^n stejný jako sequence.Length - n.
private string[] words = [
// index from start index from end
"first", // 0 ^10
"second", // 1 ^9
"third", // 2 ^8
"fourth", // 3 ^7
"fifth", // 4 ^6
"sixth", // 5 ^5
"seventh", // 6 ^4
"eighth", // 7 ^3
"ninth", // 8 ^2
"tenth" // 9 ^1
]; // 10 (or words.Length) ^0
Poslední slovo můžete načíst pomocí indexu ^1 . Pod inicializaci přidejte následující kód:
Console.WriteLine($"The last word is < {words[^1]} >."); // The last word is < tenth >.
Rozsah určuje začátek a konec rozsahu. Začátek rozsahu je inkluzivní, ale konec rozsahu je výhradní, což znamená, že začátek je součástí rozsahu, ale konec není zahrnut do rozsahu.
[0..^0] Rozsah představuje celou oblast, stejně jako [0..sequence.Length] celý rozsah.
Následující kód vytvoří podrozsah se slovy "second", "third" a "fourth". Zahrnuje od words[1] do words[3]. Prvek words[4] není v rozsahu.
string[] secondThirdFourth = words[1..4]; // contains "second", "third" and "fourth"
// < second >< third >< fourth >
foreach (var word in secondThirdFourth)
Console.Write($"< {word} >");
Console.WriteLine();
Následující kód vrátí rozsah s "devátou" a "desátou".
words[^2] a words[^1] zahrnuje. Koncový index words[^0] není zahrnutý.
string[] lastTwo = words[^2..^0]; // contains "ninth" and "tenth"
// < ninth >< tenth >
foreach (var word in lastTwo)
Console.Write($"< {word} >");
Console.WriteLine();
Následující příklady vytvářejí rozsahy, které mají otevřený začátek, konec nebo obojí:
string[] allWords = words[..]; // contains "first" through "tenth".
string[] firstPhrase = words[..4]; // contains "first" through "fourth"
string[] lastPhrase = words[6..]; // contains "seventh", "eight", "ninth" and "tenth"
// < first >< second >< third >< fourth >< fifth >< sixth >< seventh >< eighth >< ninth >< tenth >
foreach (var word in allWords)
Console.Write($"< {word} >");
Console.WriteLine();
// < first >< second >< third >< fourth >
foreach (var word in firstPhrase)
Console.Write($"< {word} >");
Console.WriteLine();
// < seventh >< eighth >< ninth >< tenth >
foreach (var word in lastPhrase)
Console.Write($"< {word} >");
Console.WriteLine();
Rozsahy nebo indexy můžete deklarovat také jako proměnné. Proměnnou je pak možné použít uvnitř [ znaků a ] znaků:
Index thirdFromEnd = ^3;
Console.WriteLine($"< {words[thirdFromEnd]} > "); // < eighth >
Range phrase = 1..4;
string[] text = words[phrase];
// < second >< third >< fourth >
foreach (var word in text)
Console.Write($"< {word} >");
Console.WriteLine();
Následující ukázka ukazuje řadu důvodů pro tyto volby. Upravit x, y, a z vyzkoušet různé kombinace. Při experimentování použijte hodnoty, kde x je menší než y, a y je menší než z pro platné kombinace. Do nové metody přidejte následující kód. Vyzkoušejte různé kombinace:
int[] numbers = [..Enumerable.Range(0, 100)];
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]}");
Nejen pole podporují indexy a rozsahy. Můžete také použít indexy a rozsahy s řetězcem, Span<T>nebo ReadOnlySpan<T>.
Převody výrazů implicitního operátoru rozsahu
Při použití syntaxe výrazu operátoru rozsahu kompilátor implicitně převede počáteční a koncové hodnoty na Index a z nich vytvoří novou Range instanci. Následující kód ukazuje příklad implicitního převodu ze syntaxe výrazu operátoru rozsahu a odpovídající explicitní alternativu:
Range implicitRange = 3..^5;
Range explicitRange = new(
start: new Index(value: 3, fromEnd: false),
end: new Index(value: 5, fromEnd: true));
if (implicitRange.Equals(explicitRange))
{
Console.WriteLine(
$"The implicit range '{implicitRange}' equals the explicit range '{explicitRange}'");
}
// Sample output:
// The implicit range '3..^5' equals the explicit range '3..^5'
Důležité
Implicitní převody z Int32 na Index vyvolají ArgumentOutOfRangeException, když je hodnota záporná. Stejně tak Index konstruktor vyvolá ArgumentOutOfRangeException , když value je parametr záporný.
Podpora typů pro indexy a rozsahy
Indexy a rozsahy poskytují jasnou a stručnou syntaxi pro přístup k jednomu prvku nebo rozsahu prvků v posloupnosti. Výraz indexu obvykle vrací typ prvků sekvence. Výraz rozsahu obvykle vrací stejný typ sekvence jako zdrojová sekvence.
Jakýkoli typ, který poskytuje indexer s parametrem Index nebo Range explicitně podporuje indexy nebo rozsahy. Indexer, který přebírá jeden Range parametr, může vrátit jiný typ sekvence, například System.Span<T>.
Důležité
Výkon kódu pomocí operátoru rozsahu závisí na typu sekvenčního operandu.
Časová složitost operátoru rozsahu závisí na typu sekvence. Pokud je například sekvence string nebo matice, je to pak kopie zadané části vstupu, takže časová složitost je O(N) (kde N je délka rozsahu). Na druhou stranu, pokud je to System.Span<T> nebo System.Memory<T>, výsledek odkazuje na stejné záložní úložiště, což znamená, že nedochází k žádné kopii a operace je O(1).
Kromě časové složitosti to způsobuje dodatečnou alokaci a kopírování, což má vliv na výkon. V kódu citlivém na výkon zvažte použití Span<T> nebo Memory<T> jako typ sekvence, protože operátor rozsahu pro ně nepřiděluje.
Typ lze spočítat , pokud má vlastnost s názvem Length nebo Count s přístupným getterem a návratovým typem int. Počitatelný typ, který explicitně nepodporuje indexy nebo rozsahy, může implicitně podporovat indexy a rozsahy. Další informace najdete v částech podpory implicitního indexu a podpory implicitního rozsahu v poznámce návrhu funkce. Rozsahy s implicitní podporou rozsahů vracejí stejný typ sekvence jako má zdrojová sekvence.
Například následující typy .NET podporují indexy i rozsahy: String, Span<T>a ReadOnlySpan<T>. Podporuje List<T> indexy, ale nepodporuje rozsahy.
Array má více nuancí chování. Pole s jednou dimenzí podporují indexy i rozsahy. Multidimenzionální pole nepodporují indexery ani rozsahy. Indexer pro vícerozměrné pole má více parametrů, nikoli jeden parametr. Jagged arrays, označované také jako pole polí, podporují rozsahy i indexery. Následující příklad ukazuje, jak iterovat obdélníkový pododdíl zahlceného pole. Iteruje oddíl uprostřed, s výjimkou prvních a posledních tří řádků a prvních a posledních dvou sloupců z každého vybraného řádku:
int[][] jagged =
[
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[10,11,12,13,14,15,16,17,18,19],
[20,21,22,23,24,25,26,27,28,29],
[30,31,32,33,34,35,36,37,38,39],
[40,41,42,43,44,45,46,47,48,49],
[50,51,52,53,54,55,56,57,58,59],
[60,61,62,63,64,65,66,67,68,69],
[70,71,72,73,74,75,76,77,78,79],
[80,81,82,83,84,85,86,87,88,89],
[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();
}
Ve všech případech operátor rozsahu pro Array přidělí pole pro uložení vrácených prvků.
Scénáře pro indexy a rozsahy
Rozsahy a indexy často použijete, když chcete analyzovat část větší sekvence. Nová syntaxe je při čtení jasnější ohledně toho, kterou část sekvence zahrnuje. Místní funkce MovingAverage přebírá Range jako svůj argument. Metoda pak vypočítá pouze tento rozsah při výpočtu min, max a průměru. Vyzkoušejte v projektu následující kód:
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))];
Poznámka k indexům rozsahu a polím
Při přebírání rozsahu z pole je výsledkem pole, které je zkopírováno z původního pole, nikoli odkazováno. Úprava hodnot ve výsledném poli nezmění hodnoty v počátečním poli.
Například:
var arrayOfFiveItems = new[] { 1, 2, 3, 4, 5 };
var firstThreeItems = arrayOfFiveItems[..3]; // contains 1,2,3
firstThreeItems[0] = 11; // now contains 11,2,3
Console.WriteLine(string.Join(",", firstThreeItems));
Console.WriteLine(string.Join(",", arrayOfFiveItems));
// output:
// 11,2,3
// 1,2,3,4,5