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


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. A taghozzáférési 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 az operátorok közé tartoznak a null-feltételes tagok hozzáférése (?.) és az indexelő hozzáférési (?[]) operátorai.

Taghozzáférés kifejezése .

A . token segítségével érhet el egy névtér vagy egy típus egy tagját, ahogy az alábbi példák 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;
  • Használja a . elemet egy minősített név létrehozásához, hogy hozzáférjen egy típushoz egy névtérben, ahogyan azt az alábbi kód is mutatja:
System.Collections.Generic.IEnumerable<int> numbers = [1, 2, 3];

A using utasítást használja, hogy a minősített nevek használata opcionális legyen.

  • A . statikus és nem statikus elérésére használható, ahogy az alábbi kód is mutatja:
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

. segítségével is hozzáférhet egy bővítménymetódushoz.

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. A C# 12-től [] kezdve egy gyűjteménykifejezést foglal magában.

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. A gyűjteménykifejezésekkel kapcsolatos információkért tekintse meg a gyűjteménykifejezések cikkét .

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

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

A szögletes zárójelek emellett listaminták kijelölésére is használhatók a mintaegyezéshez vagy a teszteléshez.

arr is ([1, 2, ..])
//Specifies that an array starts with (1, 2)

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ényt null. Más szóval:

  • Ha a a kiértékelése null értékű, akkor a a?.x vagy a a?[x] eredménye is null lesz.

  • Ha a a kiértékelése nem null értékű, akkor a a?.x vagy a?[x] eredménye ugyanaz, mint a a.x vagy a[x] eredménye, azonos sorrendben.

    Megjegyzé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. Például, ha a egy nem null értékű tömbpéldány, és x kívül esik a a határain, akkor a?[x] egy IndexOutOfRangeException-t dobna.

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 B nem kerül kiértékelésre, ha A értéke null. Továbbá, C nem kerül kiértékelésre, ha A vagy B értéke null.

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

Ha A lehet null, de B és C nem lennének null, ha A nem null, akkor csak a null-feltételes operátort kell alkalmaznia A:

A?.B.C();

Az előző példában a B értékelése és a C() meghívása nem történik meg, ha a A értéke 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 bemutatják a ?. és ?[] operátorok használatát:

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 első előző példa a null-egyesí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 a.x nem nullára állítható értéktípus a[x], akkor a T vagy a?.x a megfelelő 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 a ?? operátort, akkor a numbers?.Length < 2 kiértékelése false állapotában numbers eredményez null.

Megjegyzés:

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

A C# 14-től kezdődően a hozzárendelés a hivatkozástípusok null feltételes hozzáférési kifejezésével (?. és ?[]) megengedett. Lásd például a következő módszert:

person?.FirstName = "Scott";
messages?[5] = "five";

Az előző példa egy tulajdonsághoz és egy indexelt elemhez való hozzárendelést mutat be egy hivatkozástípuson, amely null értékű lehet. Ennek a hozzárendelésnek egy fontos jellemzője, hogy a = jobb oldalán lévő kifejezés csak akkor kerül kiértékelésre, ha a bal oldali kifejezés nem null értékű. Az alábbi kódban például a függvény GenerateNextIndex csak akkor lesz meghívva, ha a values tömb nem null értékű. Ha a values tömb értéke null, a GenerateNextIndex nem kerül meghívásra.

person?.FirstName = "Scott";
messages?[5] = "five";

Más szóval az előző kód egyenértékű az alábbi kóddal, amely a null-ellenőrzésre vonatkozó utasítást if használja:

if (values is not null)
{
    values[2] = GenerateNextIndex();
}

A hozzárendelésen kívül az összetett hozzárendelések bármely formája, például += vagy -=, engedélyezett. A növekmény (++) és a decrement (--) azonban nem engedélyezett.

Ez a fejlesztés nem sorolja be a null feltételes kifejezéseket változóként. Nem rendelhető ref hozzá, és nem rendelhető hozzá ref változóhoz, vagy nem adható át metódusnak ref vagy out argumentumként.

Szálbiztos delegált meghívás

A ?. operátorral ellenőrizheti, hogy egy delegált nem null értékű-e, és meghívhatja szálbiztos módon (például esemény kiváltásakor), ahogyan azt 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 értékű handler elem 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 futtatott kód leiratkozik az PropertyChanged eseményről, és PropertyChangednull lesz, még mielőtt handler meghívásra kerülne, az handler által hivatkozott objektum változatlan marad.

Hívási kifejezés

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

Az alábbi kód 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 kaszt kifejezések, amelyek explicit típusú átalakítást hajtanak végre, szintén zárójeleket 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 van egy int vagy Count nevű tulajdonsága egy hozzáférhető Length hozzáférővelget. A gyűjteménykifejezésekmegszámlálható típusokra is támaszkodnak.

Megjegyzés:

Az egydimenziós tömbök megszámlálhatók. A többdimenziós tömbök nem. A ^ (tartomány) operátorok .. nem használhatók többdimenziós tömbökben.

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 ^e az e eredményének implicit módon konvertálhatónak kell lennie int.

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átor ..

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 a..b kifejezésben a a és b eredményeknek implicit módon konvertálhatónak kell lenniük a Int32 vagy Index típusra.

Fontos

Az int típusú implicit konverziók Index típusra ArgumentOutOfRangeException kivételt dobnak, ha az érték negatív.

A .. operátor bármely operandusát kihagyhatja egy nyitott végű tartomány eléré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-t beleértve a végéig.
start..end Értékek a start befogadótól a end kizárólagosig.
^start.. Értékek a start ponttól kezdődően a végéig, a végétől számítva.
..^end Értékek a kezdetektől a end-ig, kizárólag a végétől visszaszámlálva.
start..^end Értékek start-tól bezárólag egészen end-ig kizárólag, a végéről számolva.
^start..^end Értékek start-tól beleértve end kizárva, mindkettő a végétől számítva.

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 .. token arra is használható, hogy bővítse egy gyűjteménykifejezés kiterjesztési elemét.

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