Taghozzáférési operátorok és kifejezések – a pont, az indexelő és a meghívási operátor.

Számos operátor és kifejezés használatával érhet el egy típustagot. Ezek az operátorok közé tartozik a taghozzáférés (.), a tömbelem vagy az indexelő hozzáférése ([]), a végponttól számított index (^), a tartomány (..), a null-feltételes operátorok (?. és ?[]) és a metódushívás (()). Ezek közé tartoznak a null-feltételes taghozzáférés (?.) és az indexelő hozzáférési (?[]) operátorai.

Taghozzáférés kifejezése .

A jogkivonattal . egy névtér vagy egy típus egy tagját érheti el, ahogy az alábbi példák is mutatják:

  • Beágyazott . névtér elérése névtéren belül, ahogy az irányelv alábbi példája using is mutatja:
using System.Collections.Generic;
  • Minősített . név létrehozásához használjon egy névtéren belüli típust, ahogyan az az alábbi kódban is látható:
System.Collections.Generic.IEnumerable<int> numbers = [1, 2, 3];

using A minősített nevek használata nem kötelező.

List<double> constants =
[
    Math.PI,
    Math.E
];
Console.WriteLine($"{constants.Count} values to show:");
Console.WriteLine(string.Join(", ", constants));
// Output:
// 2 values to show:
// 3.14159265358979, 2.71828182845905

Bővítménymetódushoz is . hozzáférhet.

Indexelő operátor []

A szögletes zárójeleket []általában tömbök, indexelők vagy mutatóelemek elérésére használják.

Tömbhozzáférés

Az alábbi példa a tömbelemek elérését mutatja be:

int[] fib = new int[10];
fib[0] = fib[1] = 1;
for (int i = 2; i < fib.Length; i++)
{
    fib[i] = fib[i - 1] + fib[i - 2];
}
Console.WriteLine(fib[fib.Length - 1]);  // output: 55

double[,] matrix = new double[2,2];
matrix[0,0] = 1.0;
matrix[0,1] = 2.0;
matrix[1,0] = matrix[1,1] = 3.0;
var determinant = matrix[0,0] * matrix[1,1] - matrix[1,0] * matrix[0,1];
Console.WriteLine(determinant);  // output: -3

Ha egy tömbindex kívül esik egy tömb megfelelő dimenziójának határain, akkor a függvény egy IndexOutOfRangeException értéket ad.

Ahogy az előző példában is látható, szögletes zárójeleket is használhat tömbtípus deklarálásakor vagy tömbpéldány példányosításakor.

A tömbökről további információt a Tömbök című témakörben talál.

Indexelői hozzáférés

Az alábbi példa a .NET Dictionary<TKey,TValue> típust használja az indexelő hozzáférésének szemléltetésére:

var dict = new Dictionary<string, double>();
dict["one"] = 1;
dict["pi"] = Math.PI;
Console.WriteLine(dict["one"] + dict["pi"]);  // output: 4.14159265358979

Az indexelők lehetővé teszik a felhasználó által definiált típusú példányok indexelését a tömbindexeléshez hasonlóan. A tömbindexekkel ellentétben, amelyeknek egész számnak kell lenniük, az indexelő paraméterek bármilyen típusúnak deklarálhatók.

További információ az indexelőkről: Indexelők.

A [] egyéb használati módjai

A mutatóelem-hozzáféréssel kapcsolatos információkért tekintse meg a Mutatóelem-hozzáférés operátor [] szakaszát a Mutatóhoz kapcsolódó operátorok című cikkben.

Az attribútumok megadásához szögletes zárójeleket is használhat:

[System.Diagnostics.Conditional("DEBUG")]
void TraceMethod() {}

Null feltételes operátorok ?. és ?[]

A null feltételes operátor csak akkor alkalmaz taghozzáférési () vagy elemhozzáférési (?[]) műveletet az operandusra, ha az operandus nem null értékű; ellenkező esetben visszaadja a függvénytnull.?. Tehát:

  • Ha a a kiértékelés eredménye nullvagy eredménye a?.xa?[x] .null

  • Ha a a kiértékelés nem null értékű, akkor az eredmény vagy annak eredménye a.xa?.xa?[x]a[x]megegyezik.

    Feljegyzés

    Ha a.x vagy a[x] kivesz egy kivételt, a?.x vagy a?[x] ugyanazt a kivételt a nem null aértékre veti. Ha például a egy nem null értékű tömbpéldány, és x kívül esik a határán a, a?[x] akkor a függvény egy IndexOutOfRangeException.

A null feltételes operátorok rövidzárolást jelentenek. Vagyis ha egy feltételes tag- vagy elemhozzáférési műveletlánc egyik művelete visszatér null, a lánc többi része nem lesz végrehajtva. A következő példában a kiértékelés nem történik meg, B ha a kiértékelés CnullBAnull eredménye a következő:A

A?.B?.Do(C);
A?.B?[C];

Ha A lehet null, de BC nem lenne null, ha az A nem null, csak a null-feltételes operátort kell alkalmaznia a következőre A:

A?.B.C();

Az előző példában a függvény nem értékeli ki a függvényt, B és C() nem hívja meg, ha A null. Ha azonban a láncolt tag hozzáférése megszakad, például zárójelek szerint, a (A?.B).C()rövidzárolás nem történik meg.

Az alábbi példák az operátorok és ?[] az ?. operátorok használatát mutatják be:

double SumNumbers(List<double[]> setsOfNumbers, int indexOfSetToSum)
{
    return setsOfNumbers?[indexOfSetToSum]?.Sum() ?? double.NaN;
}

var sum1 = SumNumbers(null, 0);
Console.WriteLine(sum1);  // output: NaN

List<double[]?> numberSets =
[
    [1.0, 2.0, 3.0],
    null
];

var sum2 = SumNumbers(numberSets, 0);
Console.WriteLine(sum2);  // output: 6

var sum3 = SumNumbers(numberSets, 1);
Console.WriteLine(sum3);  // output: NaN
namespace MemberAccessOperators2;

public static class NullConditionalShortCircuiting
{
    public static void Main()
    {
        Person? person = null;
        person?.Name.Write(); // no output: Write() is not called due to short-circuit.
        try
        {
            (person?.Name).Write();
        }
        catch (NullReferenceException)
        {
            Console.WriteLine("NullReferenceException");
        }; // output: NullReferenceException
    }
}

public class Person
{
    public required FullName Name { get; set; }
}

public class FullName
{
    public required string FirstName { get; set; }
    public required string LastName { get; set; }
    public void Write() => Console.WriteLine($"{FirstName} {LastName}");
}

Az előző két példa közül az első a null-szenesítő operátort ?? is használja egy alternatív kifejezés megadására, amely kiértékelhető abban az esetben, ha a null-feltételes művelet eredménye .null

Ha a.x nem null értékűT, a?.x vagy a?[x] a megfelelő null értékű.T?a[x] Ha típuskifejezésre Tvan szüksége, alkalmazza a null-szenesítő operátort ?? egy null-feltételes kifejezésre, ahogyan az alábbi példa mutatja:

int GetSumOfFirstTwoOrDefault(int[]? numbers)
{
    if ((numbers?.Length ?? 0) < 2)
    {
        return 0;
    }
    return numbers[0] + numbers[1];
}

Console.WriteLine(GetSumOfFirstTwoOrDefault(null));  // output: 0
Console.WriteLine(GetSumOfFirstTwoOrDefault([]));  // output: 0
Console.WriteLine(GetSumOfFirstTwoOrDefault([3, 4, 5]));  // output: 7

Az előző példában, ha nem használja az operátort ?? , kiértékeli, numbers?.Length < 2 hogy false mikor numbers van null.

Feljegyzés

Az ?. operátor legfeljebb egyszer értékeli ki a bal oldali operandusát, garantálva, hogy nem módosítható null a nem null értékűként való ellenőrzés után.

A null feltételes tag hozzáférési operátorát ?. Elvis operátornak is nevezik.

Szálbiztos delegálás

?. Az operátorral ellenőrizheti, hogy egy meghatalmazott nem null értékű-e, és meghívja-e szálbiztos módon (például esemény létrehozásakor), ahogyan az alábbi kód mutatja:

PropertyChanged?.Invoke(…)

Ez a kód egyenértékű a következő kóddal:

var handler = this.PropertyChanged;
if (handler != null)
{
    handler(…);
}

Az előző példa egy szálbiztos módszer annak biztosítására, hogy csak egy nem null handler értékű hívás legyen meghívva. Mivel a delegált példányok nem módosíthatók, egyetlen szál sem módosíthatja a handler helyi változó által hivatkozott objektumot. Különösen akkor, ha egy másik szál által végrehajtott kód leiratkozik az PropertyChanged eseményről, és PropertyChangednull a rendszer meghívja handler , az általa handler hivatkozott objektumot nem érinti.

Invocation expression ()

Zárójelek használatával meghívhat egy metódust, vagy meghívhat egy meghatalmazottat. ()

Az alábbi példa bemutatja, hogyan hívhat meg egy metódust argumentumokkal vagy anélkül, és hogyan hívhat meg meghatalmazottat:

Action<int> display = s => Console.WriteLine(s);

List<int> numbers =
[
    10,
    17
];
display(numbers.Count);   // output: 2

numbers.Clear();
display(numbers.Count);   // output: 0

Zárójeleket is használhat, amikor konstruktort hív meg az new operátorral.

A () egyéb használati módjai

Zárójelek használatával is módosíthatja a kifejezések műveleteinek kiértékelési sorrendjét. További információ: C# operátorok.

Az explicit típusú átalakításokat végző öntött kifejezések zárójeleket is használnak.

Index a záró operátorból ^

Az index- és tartományoperátorok megszámlálható típussal használhatók. A megszámlálható típus egy olyan típus, amelynek vagy egy akadálymentes get tartozéka van, vagy Length elnevezett Count tulajdonsággal rendelkezikint. A gyűjteménykifejezések megszámlálható típusokra is támaszkodnak.

Az ^ operátor egy sorozat végéről jelzi az elem pozícióját. A sorozat hossza length^n esetén a sorozat kezdetétől eltolással length - n rendelkező elemre mutat. Például egy sorozat utolsó elemére mutat, ^1 és ^length a sorozat első elemére mutat.

int[] xs = [0, 10, 20, 30, 40];
int last = xs[^1];
Console.WriteLine(last);  // output: 40

List<string> lines = ["one", "two", "three", "four"];
string prelast = lines[^2];
Console.WriteLine(prelast);  // output: three

string word = "Twenty";
Index toFirst = ^word.Length;
char first = word[toFirst];
Console.WriteLine(first);  // output: T

Ahogy az előző példa is mutatja, a kifejezés ^e a System.Index típus. A kifejezésben ^eaz eredménynek e implicit módon konvertálhatónak intkell lennie .

Az operátort a ^tartomány operátorával is használhatja indextartomány létrehozásához. További információ: Indexek és tartományok.

A C# 13-tól kezdődően a záró operátor indexe használható az objektum inicializálójában.

Tartomány operátora ..

Az .. operátor egy indextartomány kezdetét és végét adja meg operandusként. A bal oldali operandus egy tartomány befogadó kezdete. A jobb oldali operandus egy tartomány kizárólagos vége. Az operandusok bármelyike lehet index egy sorozat elejétől vagy végétől kezdve, ahogy az alábbi példa mutatja:

int[] numbers = [0, 10, 20, 30, 40, 50];
int start = 1;
int amountToTake = 3;
int[] subset = numbers[start..(start + amountToTake)];
Display(subset);  // output: 10 20 30

int margin = 1;
int[] inner = numbers[margin..^margin];
Display(inner);  // output: 10 20 30 40

string line = "one two three";
int amountToTakeFromEnd = 5;
Range endIndices = ^amountToTakeFromEnd..^0;
string end = line[endIndices];
Console.WriteLine(end);  // output: three

void Display<T>(IEnumerable<T> xs) => Console.WriteLine(string.Join(" ", xs));

Ahogy az előző példa is mutatja, a kifejezés a..b a System.Range típus. A kifejezésben a..baz eredménynek ab implicit módon konvertálhatónak kell lennie a következőre Int32 : vagy Index.

Fontos

Implicit konverziók a int negatív értéktől a dobásig IndexArgumentOutOfRangeException .

Az operátor bármelyik operandusát .. kihagyhatja egy nyitott végű tartomány beszerzéséhez:

  • a.. egyenértékű a a..^0
  • ..b egyenértékű a 0..b
  • .. egyenértékű a 0..^0
int[] numbers = [0, 10, 20, 30, 40, 50];
int amountToDrop = numbers.Length / 2;

int[] rightHalf = numbers[amountToDrop..];
Display(rightHalf);  // output: 30 40 50

int[] leftHalf = numbers[..^amountToDrop];
Display(leftHalf);  // output: 0 10 20

int[] all = numbers[..];
Display(all);  // output: 0 10 20 30 40 50

void Display<T>(IEnumerable<T> xs) => Console.WriteLine(string.Join(" ", xs));

Az alábbi táblázat a gyűjteménytartományok kifejezésének különböző módjait mutatja be:

Tartomány operátorának kifejezése Leírás
.. A gyűjtemény összes értéke.
..end Értékek az elejétől a end kizárólagosig.
start.. Értékek a start befogadótól a végéig.
start..end Értékek a start befogadótól a end kizárólagosig.
^start.. Értékek a start befogadótól a végéig, a végétől számítva.
..^end Értékek a kezdetektől a end kizárólagos számlálásig a végétől kezdve.
start..^end Értékek a befogadótól start a end kizárólagos számlálásig a végétől kezdve.
^start..^end Értékek a befogadótól start a end kizárólagos számlálásig a végétől kezdve.

Az alábbi példa az előző táblázatban bemutatott összes tartomány használatának hatását mutatja be:

int[] oneThroughTen =
[
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10
];

Write(oneThroughTen, ..);
Write(oneThroughTen, ..3);
Write(oneThroughTen, 2..);
Write(oneThroughTen, 3..5);
Write(oneThroughTen, ^2..);
Write(oneThroughTen, ..^3);
Write(oneThroughTen, 3..^4);
Write(oneThroughTen, ^4..^2);

static void Write(int[] values, Range range) =>
    Console.WriteLine($"{range}:\t{string.Join(", ", values[range])}");
// Sample output:
//      0..^0:      1, 2, 3, 4, 5, 6, 7, 8, 9, 10
//      0..3:       1, 2, 3
//      2..^0:      3, 4, 5, 6, 7, 8, 9, 10
//      3..5:       4, 5
//      ^2..^0:     9, 10
//      0..^3:      1, 2, 3, 4, 5, 6, 7
//      3..^4:      4, 5, 6
//      ^4..^2:     7, 8

További információ: Indexek és tartományok.

A .. rendszer a jogkivonatot a gyűjteménykifejezések oldalpár-operátoraként is használja.

Operátorok túlterhelése

A ., (), ^és .. operátorok nem terhelhetők túl. Az [] operátort nem túlterhelhető operátornak is tekintik. Az indexelők segítségével támogathatja a felhasználó által definiált típusok indexelését.

C# nyelvspecifikáció

További információt a C# nyelvspecifikációjának alábbi szakaszaiban talál:

Az indexekkel és tartományokkal kapcsolatos további információkért tekintse meg a funkciójavaslat megjegyzését.

Lásd még