Megosztás a következőn keresztül:


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.

Ebben az oktatóanyagban megtanulhatja, hogyan:

  • Használja a sorozatban lévő tartományok szintaxisát.
  • Implicit módon definiáljon egy Range.
  • Megismerheti az egyes sorozatok elején és végének tervezési döntéseit.
  • Ismerje meg a Index és Range típusok forgatókönyveit.

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:

Kezdjük az indexek szabályaival. Tekintsük 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 vet fel, ahogy sequence[sequence.Length] az is. Bármely szám nesetén az index ^n megegyezik sequence.Length - na .

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

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]} >."); // The last word is < tenth >.

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 "second", a "third" és a "fourth" szavakkal. Ez magában foglalja words[1]-tól words[3]-ig. Az elem words[4] nincs a tartományban.

string[] secondThirdFourth = words[1..4]; // contains "second", "third" and "fourth"

// < second >< third >< fourth >
foreach (var word in secondThirdFourth)
    Console.Write($"< {word} >"); 
Console.WriteLine();

Az alábbi kód a tartományt a "kilencedik" és a "tized" értékkel adja vissza. words[^2] és words[^1] tartalmazza. A záró index words[^0] nem szerepel a fájlban.

 string[] lastTwo = words[^2..^0]; // contains "ninth" and "tenth"

 // < ninth >< tenth >
 foreach (var word in lastTwo)
     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 "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();

Tartományokat vagy indexeket változóként is deklarálhat. A változó ezután a [ és ] karakterek között használható.

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();

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, hogy kipróbálja 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 karakterlánccalSpan<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óprogram automatikusan átalakítja a kezdő és a záró értékeket Index-vé, é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 és Index között ArgumentOutOfRangeException-et dob, amikor az érték negatív. Hasonlóképpen, a Index konstruktor negatív ArgumentOutOfRangeException értéket ad, amikor a value paraméter negatív.

Típus támogatás indexekhez és tartományokhoz

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 Index paramétert Range 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). Amennyiben pedig egy System.Span<T> vagy egy System.Memory<T> van, 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 többletfoglalásokat és másolatokat is okoz, ami hatással van a teljesítményre. Teljesítményérzékeny kódban érdemes megfontolni a Span<T> vagy Memory<T> használatát sorozattípusként, mivel a tartomány operátora nem foglal el memóriahelyet számukra.

Egy típus számlálható, ha rendelkezik egy elnevezett Length vagy Count tulajdonsággal, amely elérhető lekérővel és visszatérési típussal rendelkezik int-ként. 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énekimplicit indextámogatási és implicit tartománytámogatási szakaszait. Az implicit tartománytámogatást alkalmazó tartományok ugyanazt a sorozattípust adják vissza, mint a forrássorozat.

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ámogatja az indexeket, de nem támogatja a tartományokat.

Array árnyaltabb viselkedéssel rendelkezik. 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. Szaggatott tömbök, más néven tömbök tömbjei, tartományokat és indexelőket egyaránt 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 egyértelműbbé teszi, hogy a sorozat melyik részéről van szó. 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

Lásd még