Indexek és tartományok
A tartományok és indexek tömör szintaxist biztosítanak az egyes elemek vagy tartományok sorozatban való eléréséhez.
Az oktatóanyag segítségével megtanulhatja a következőket:
Indexek és tartományok nyelvi támogatása
Az indexek és a tartományok tömör szintaxist biztosítanak az egyes elemek vagy tartományok sorozatban való eléréséhez.
Ez a nyelvi támogatás két új típusra és két új operátorra támaszkodik:
- System.Index egy indexet egy sorozatba jelöl.
- A záró operátor indexe
^
, amely azt határozza meg, hogy egy index a sorozat végéhez képest van-e. - System.Range egy sorozat altartományát jelöli.
- A tartomány operátora
..
, amely egy tartomány kezdetét és végét adja meg operandusként.
Kezdjük az indexek szabályaival. Fontolja meg a tömböt sequence
. Az 0
index megegyezik sequence[0]
a . Az ^0
index megegyezik sequence[sequence.Length]
a . A kifejezés sequence[^0]
kivételt okoz, ahogy sequence[sequence.Length]
az is. Bármely szám n
esetén az index ^n
megegyezik sequence.Length - n
a .
string[] words = [
// index from start index from end
"The", // 0 ^9
"quick", // 1 ^8
"brown", // 2 ^7
"fox", // 3 ^6
"jumps", // 4 ^5
"over", // 5 ^4
"the", // 6 ^3
"lazy", // 7 ^2
"dog" // 8 ^1
]; // 9 (or words.Length) ^0
Az utolsó szót lekérheti az ^1
indexszel. Adja hozzá a következő kódot az inicializálás alatt:
Console.WriteLine($"The last word is {words[^1]}");
A tartomány egy tartomány kezdetét és végét határozza meg. A tartomány kezdete magában foglalja a tartományt, de a tartomány vége kizárólagos, ami azt jelenti, hogy a kezdő a tartomány része, de a vége nem szerepel a tartományban. A tartomány [0..^0]
a teljes tartományt jelöli, ugyanúgy, mint [0..sequence.Length]
a teljes tartományt.
Az alábbi kód egy altartományt hoz létre a "gyors", a "barna" és a "róka" szavakkal. Ez magában foglalja words[1]
a .words[3]
Az elem words[4]
nincs a tartományban.
string[] quickBrownFox = words[1..4];
foreach (var word in quickBrownFox)
Console.Write($"< {word} >");
Console.WriteLine();
Az alábbi kód a "lusta" és a "kutya" tartományt adja vissza. words[^2]
Tartalmazza és words[^1]
. A záró index words[^0]
nem szerepel a fájlban. Adja hozzá a következő kódot is:
string[] lazyDog = words[^2..^0];
foreach (var word in lazyDog)
Console.Write($"< {word} >");
Console.WriteLine();
Az alábbi példák olyan tartományokat hoznak létre, amelyek nyitottak a kezdéshez, a befejezéshez vagy mindkettőhöz:
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();
Tartományokat vagy indexeket változóként is deklarálhat. A változó ezután a következő karaktereken ]
belül [
használható:
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();
Az alábbi minta számos érvet mutat be ezeknek a választási lehetőségeknek a céljából. Módosítsa x
, y
és z
próbálja ki a különböző kombinációkat. Kísérletezéskor olyan értékeket használjon, ahol x
kisebb, mint y
, és y
kisebb, mint z
az érvényes kombinációk esetében. Adja hozzá a következő kódot egy új metódushoz. Próbálja ki a különböző kombinációkat:
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]}");
Nemcsak a tömbök támogatják az indexeket és a tartományokat. Az indexeket és tartományokat sztringgel Span<T>vagy .ReadOnlySpan<T>
Implicit tartomány operátorkifejezéseinek konvertálása
A tartományoperátor kifejezésszintaxisának használatakor a fordító implicit módon konvertálja a kezdő és a záró értékeket, Index és ezekből létrehoz egy új Range példányt. Az alábbi kód egy példa implicit konverziót mutat be a tartomány operátorkifejezésének szintaxisából, és annak megfelelő explicit alternatíváját:
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'
Fontos
Implicit konverziók a Int32 negatív értéktől a dobásig IndexArgumentOutOfRangeException . Hasonlóképpen, a Index
konstruktor negatív ArgumentOutOfRangeException
értéket ad, amikor a value
paraméter negatív.
Indexek és tartományok támogatásának beírása
Az indexek és a tartományok egyértelmű, tömör szintaxist biztosítanak, hogy egyetlen elemet vagy elemtartományt érhessenek el egy sorozatban. Az indexkifejezés általában egy sorozat elemeinek típusát adja vissza. A tartománykifejezések általában ugyanazt a sorozattípust adják vissza, mint a forrásütemezés.
Minden olyan típus, amely egy indexelőt vagy Range paramétert Index biztosít, kifejezetten támogatja az indexeket vagy tartományokat. Az egyetlen Range paramétert használó indexelők eltérő sorozattípust adhatnak vissza, például System.Span<T>.
Fontos
A kód tartomány operátorral történő teljesítménye a sorozatoperndus típusától függ.
A tartomány operátorának időösszetettsége a sorozat típusától függ. Ha például a sorozat egy string
vagy tömb, akkor az eredmény a bemenet megadott szakaszának másolata, így az idő összetettsége O(N) (ahol N a tartomány hossza). Ha viszont egy System.Span<T> vagy egy System.Memory<T>, az eredmény ugyanarra a háttértárra hivatkozik, ami azt jelenti, hogy nincs másolat, és a művelet O(1).
Az idő összetettsége mellett ez további foglalásokat és másolatokat is okoz, ami hatással van a teljesítményre. A teljesítményérzékeny kódban érdemes lehet a sorozattípust használni Span<T>
vagy Memory<T>
használni, mivel a tartomány operátora nem foglalja le őket.
Egy típus akkor számítható meg, ha rendelkezik egy elnevezett Length
tulajdonsággal, vagy Count
egy akadálymentes lekérővel és egy visszatérési típussal int
. Az indexeket vagy tartományokat explicit módon nem támogató megszámlálható típus implicit támogatást nyújthat számukra. További információkért tekintse meg a funkciójavaslat megjegyzésének implicit indextámogatási és implicit tartománytámogatási szakaszait. Az implicit tartománytámogatást használó tartományok ugyanazt a sorozattípust adják vissza, mint a forrásütemezés.
A következő .NET-típusok például az indexeket és a tartományokat is támogatják: String, Span<T>és ReadOnlySpan<T>. A List<T> támogatott indexek nem támogatják a tartományokat.
Array árnyaltabb viselkedést ad. Az egydimenziós tömbök az indexeket és a tartományokat is támogatják. A többdimenziós tömbök nem támogatják az indexelőket és a tartományokat. A többdimenziós tömb indexelője több paraméterrel rendelkezik, nem egyetlen paraméterrel. A szaggatott tömbök, más néven tömbök mind tartományokat, mind indexelőket támogatnak. Az alábbi példa bemutatja, hogyan lehet egy szaggatott tömb négyszögletes alszakaszát iterálni. A középen lévő szakaszt iterálja, kivéve az első és az utolsó három sort, valamint az első és az utolsó két oszlopot az egyes kijelölt sorokból:
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();
}
A visszaadott elemek tárolásához Array a tartomány operátora minden esetben lefoglal egy tömböt.
Forgatókönyvek indexekhez és tartományokhoz
Gyakran használ tartományokat és indexeket, ha egy nagyobb sorozat egy részét szeretné elemezni. Az új szintaxis pontosabban beolvassa a sorozat melyik részét. A helyi függvény MovingAverage
argumentumként egy függvényt Range vesz fel. A metódus ezután csak ezt a tartományt veszi számba a minimális, a maximális és az átlag kiszámításakor. Próbálja ki a következő kódot a projektben:
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))];
Megjegyzés a tartományindexekről és tömbökről
Ha tartományt vesz fel egy tömbből, az eredmény egy olyan tömb, amelyet a rendszer a kezdeti tömbből másol, és nem hivatkozik rá. Az eredményként kapott tömb értékeinek módosítása nem módosítja a kezdeti tömb értékeit.
Például:
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