Sdílet prostřednictvím


Operátory a výrazy přístupu členů – tečky, indexer a operátory volání.

Pro přístup k členu typu použijete několik operátorů a výrazů. Mezi tyto operátory patří přístup člena (.), prvek pole nebo přístup indexeru ([]), index-from-end (^), range (..), operátory s podmíněnou hodnotou null (?. a ?[]) a vyvolání metody (()). Patří mezi ně operátory přístupu s podmíněným přístupem s hodnotou null (?.) a indexerem (?[]).

Výraz přístupu člena .

Token použijete . pro přístup k členu oboru názvů nebo typu, jak ukazují následující příklady:

  • Slouží . k přístupu k vnořenému oboru názvů v rámci oboru názvů, jak ukazuje následující příklad direktivyusing:
using System.Collections.Generic;
  • Slouží . k vytvoření kvalifikovaného názvu pro přístup k typu v rámci oboru názvů, jak ukazuje následující kód:
System.Collections.Generic.IEnumerable<int> numbers = [1, 2, 3];

Použití direktivy using k použití kvalifikovaných názvů volitelné.

  • Používá . se pro přístup ke členům typu, statickým a nestatickým, jak ukazuje následující kód:
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

Můžete také použít . přístup k metodě rozšíření.

Operátor indexeru []

Hranaté závorky , []se obvykle používají pro přístup k prvkům pole, indexeru nebo ukazatele. Počínaje jazykem C# 12 [] uzavře výraz kolekce.

Přístup k poli

Následující příklad ukazuje přístup k prvkům pole:

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

Pokud je index pole mimo hranice odpovídající dimenze pole, IndexOutOfRangeException vyvolá se.

Jak ukazuje předchozí příklad, použijete také hranaté závorky, když deklarujete typ pole nebo vytvoříte instanci pole.

Další informace o polích naleznete v tématu Pole.

Přístup k indexeru

Následující příklad používá typ .NET Dictionary<TKey,TValue> k předvedení přístupu indexeru:

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

Indexery umožňují indexovat instance uživatelem definovaného typu podobným způsobem jako indexování pole. Na rozdíl od indexů pole, které musí být celé číslo, lze parametry indexeru deklarovat jako jakýkoli typ.

Další informace o indexerech naleznete v tématu Indexery.

Další využití []

Informace o přístupu k prvkům ukazatele naleznete v části Přístup k prvku ukazatele [] oddílu Ukazatele související operátory článku. Informace o výrazech kolekce naleznete v článku výrazy kolekce.

K určení atributů také použijete hranaté závorky:

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

Podmíněné operátory ?. s hodnotou Null a ?[]

Podmíněný operátor s hodnotou null použije operaci přístupu člena (?.) nebo elementu (?[]) na svůj operand pouze v případě, že se tento operand vyhodnotí jako nenulový; v opačném případě vrátí null. Jinými slovy:

  • Pokud a se vyhodnotí jako null, výsledek a?.x nebo a?[x] je null.

  • Pokud a se vyhodnotí na hodnotu non-null, výsledek a?.x nebo a?[x] je stejný jako výsledek a.x nebo a[x], v uvedeném pořadí.

    Poznámka:

    Pokud a.x nebo a[x] vyvolá výjimku, a?.x nebo a?[x] by vyvolá stejnou výjimku pro non-null a. Pokud a je například instance pole, která není null a x je mimo hranice a, a?[x] vyvolá výjimku IndexOutOfRangeException.

Podmíněné operátory s hodnotou null jsou zkratové. To znamená, že pokud jedna operace v řetězci operací přístupu podmíněného člena nebo elementu vrátí null, zbytek řetězce se nespustí. V následujícím příkladu se nevyhodnocuje, B pokud A se vyhodnotí null jako a C nevyhodnocuje, jestli A nebo B se vyhodnotí jako null:

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

Pokud A může mít hodnotu null, ale B pokud C hodnota A není null, musíte použít pouze podmíněný operátor null na A:

A?.B.C();

V předchozím příkladu se nevyhodnocuje a C() není volána, B pokud A má hodnotu null. Pokud je však přístup zřetězeného člena přerušen, například závorky jako v (A?.B).C()případě , zkratování se nestane.

Následující příklady ukazují použití ?. operátorů a ?[] operátorů:

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}");
}

První z předchozích dvou příkladů také používá operátor ?? null-coalescing k určení alternativního výrazu, který se má vyhodnotit v případě, že je nullvýsledkem operace s podmínkou null .

Je-li a.x nebo a[x] je typu hodnoty, která není nullable T, a?.x nebo a?[x] je odpovídající typ T?hodnoty nullable . Pokud potřebujete výraz typu T, použijte operátor ?? null-coalescing na podmíněný výraz null, jak ukazuje následující příklad:

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

Pokud v předchozím příkladu operátor nepoužíváte ?? , numbers?.Length < 2 vyhodnotí se, false kdy numbers je null.

Poznámka:

Operátor ?. vyhodnotí levý operand ne více než jednou a zaručuje, že ho nelze změnit na null po ověření jako nenulové.

Operátor přístupu ?. k podmíněnému členu s hodnotou null se také označuje jako operátor Elvis.

Vyvolání delegáta bezpečného pro přístup z více vláken

Pomocí operátoru ?. zkontrolujte, jestli delegát nemá hodnotu null a vyvolá ho bezpečným způsobem vlákna (například při vyvolání události), jak ukazuje následující kód:

PropertyChanged?.Invoke(…)

Tento kód je ekvivalentní následujícímu kódu:

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

Předchozí příklad představuje bezpečný způsob, jak zajistit, že se vyvolá pouze nenulová handler hodnota. Vzhledem k tomu, že instance delegátů jsou neměnné, nemůže vlákno změnit objekt odkazovaný místní proměnnou handler . Zejména pokud kód spuštěný jiným vláknem odhlásí událost PropertyChanged a PropertyChanged stane se null před handler vyvoláním, objekt odkazovaný handler na něj zůstane nedotčen.

Výraz vyvolání ()

K volání metody nebo vyvolání delegáta použijte závorky().

Následující příklad ukazuje, jak volat metodu s argumenty nebo bez argumentů a vyvolat delegáta:

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

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

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

Při vyvolání konstruktoru s operátorem new také použijete závorky.

Další využití ()

Pomocí závorek také upravíte pořadí, ve kterém se mají vyhodnocovat operace ve výrazu. Další informace najdete v tématu Operátory jazyka C#.

Přetypování výrazů, které provádějí explicitní převody typů, také používají závorky.

Index od koncového operátoru ^

Operátory indexu a rozsahu lze použít s typem, který lze spočítat. Počítaný typ je typ, který má int vlastnost s názvem buď Count nebo Length s přístupným get příslušenstvím. Výrazy kolekce také spoléhají na počítané typy.

Operátor ^ označuje pozici prvku od konce sekvence. Pro sekvenci délky length^n odkazuje na prvek s posunem length - n od začátku sekvence. Například ^1 odkazuje na poslední prvek sekvence a ^length odkazuje na první prvek sekvence.

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

Jak ukazuje předchozí příklad, výraz ^e je typu System.Index . Ve výrazu ^emusí být výsledek e implicitně konvertibilní na int.

Pomocí operátoru rozsahu ^ můžete také vytvořit rozsah indexů. Další informace naleznete v tématu Indexy a rozsahy.

Počínaje jazykem C# 13 lze index z koncového operátoru použít v inicializátoru objektů.

Operátor rozsahu ..

Operátor .. určuje začátek a konec rozsahu indexů jako jeho operandy. Levý operand je inkluzivním začátkem rozsahu. Pravý operand je výhradním koncem rozsahu. Jeden z operandů může být indexem od začátku nebo od konce sekvence, jak ukazuje následující příklad:

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

Jak ukazuje předchozí příklad, výraz a..b je typu System.Range . Ve výrazu a..bmusí být výsledky a a b musí být implicitně konvertibilní na Int32 nebo Index.

Důležité

Implicitní převody z int hodnoty na Index vyvolání, když je hodnota záporná ArgumentOutOfRangeException .

Pokud chcete získat otevřený rozsah, můžete vynechat některý z operandů .. operátoru:

  • a.. je ekvivalentní a..^0
  • ..b je ekvivalentní 0..b
  • .. je ekvivalentní 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));

Následující tabulka ukazuje různé způsoby vyjádření rozsahů kolekcí:

Výraz operátoru rozsahu Popis
.. Všechny hodnoty v kolekci.
..end Hodnoty od začátku až po end výhradně.
start.. Hodnoty od start inkluzí do konce.
start..end Hodnoty od inkluzivního start end po výhradně.
^start.. Hodnoty od inkluzivního start po koncové počítání od konce.
..^end Hodnoty od začátku po end výhradní počítání od konce.
start..^end Hodnoty od start inkluzivního po end výhradní počítání od konce.
^start..^end Hodnoty od start inkluzivního po end výhradně oba počítání od konce.

Následující příklad ukazuje účinek použití všech oblastí uvedených v předchozí tabulce:

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

Další informace naleznete v tématu Indexy a rozsahy.

Token .. se také používá pro prvek šíření ve výrazu kolekce.

Přetížení operátoru

Operátory ., (), ^a .. nelze přetížit. Operátor [] je také považován za nepřetížitelný operátor. Pomocí indexerů můžete podporovat indexování s uživatelsky definovanými typy.

specifikace jazyka C#

Další informace najdete v následujících částech specifikace jazyka C#:

Další informace o indexech a rozsazích najdete v poznámce k návrhu funkce.

Viz také