共用方式為


成員存取運算子與表達式——點、索引器與調用運算元

您可以使用數個運算子和表示式來存取類型成員。 成員存取運算子包括成員存取(.)、陣列元素或索引器存取([])、從結尾索引(^)、範圍(..)、空條件運算子(?.?[])和方法呼叫(())。 這些運算子包括 null 條件式 成員存取(?.),和索引器存取(?[])運算子。

C# 語言參考資料記錄了 C# 語言最新版本。 同時也包含即將推出語言版本公開預覽功能的初步文件。

文件中標示了語言最近三個版本或目前公開預覽版中首次引入的任何功能。

小提示

欲查詢某功能何時首次在 C# 中引入,請參閱 C# 語言版本歷史的條目。

成員存取表達式 .

使用 . 該標記存取命名空間或型態的成員,以下範例可示範:

  • 使用 . 來存取命名空間中的巢狀命名空間,如下列 using 指令範例所示:
using System.Collections.Generic;
  • 使用 . 來形成 限定名稱 來存取命名空間內的類型,如下列程式代碼所示:
System.Collections.Generic.IEnumerable<int> numbers = [1, 2, 3];

using使用指令來使使用限定名稱成為可選項。

  • 使用 . 來存取 類型成員、靜態和非靜態,如下列程式代碼所示:
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

你也可以用 . 來存取 延伸會員

索引器運算子 []

方括號, []通常會存取陣列、索引器或指標元素。 從 C# 12 開始,[] 用來括住 集合運算式

陣組存取

下列範例示範如何存取陣列元素:

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

若陣列索引超出陣列對應維度範圍,執行時會拋出一個 IndexOutOfRangeException

如上述範例所示,當您宣告數位類型或具現化數位列實例時,也會使用方括號。

如需有關陣列的詳細資訊,請參閱 陣列

索引器存取

下列範例會使用 .NET Dictionary<TKey,TValue> 類型來示範索引器存取:

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

索引器允許你以類似陣列索引的方式,索引使用者定義型別的實例。 與必須是整數的陣列索引不同,索引器參數可以宣告為任何類型。

如需索引器的詳細資訊,請參閱 索引器

[] 的其他用法

如需指標元素存取的相關信息,請參閱指標相關運算符一文的 Pointer 元素存取運算子 [] 區段。 如需集合表達式的相關信息,請參閱 集合表達式 一文。

您也可以使用方括弧來指定 屬性

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

此外,使用方括號標示 清單模式 ,用於模式匹配或測試。

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

空值條件運算子 ?.?[]

只有當該運算元值值為非空時,空條件運算子才會對其操作數套用 成員存取 ()?.元素存取?[])操作。 否則會傳回 null。 換句話說:

  • 如果 a 評估為 null,則 a?.xa?[x] 的結果為 null

  • 如果a評估為非空,則a?.xa?[x]的結果分別與a.xa[x]的結果相同。

    備註

    a.xa[x] 拋出例外, a?.xa?[x] 對非空 a拋出相同例外。 例如,若 a 是非空陣列實例且 x 超出 的aa?[x]範圍,則拋出一個 IndexOutOfRangeException

空值條件運算子是短路運算子。 換句話說,如果條件式成員或項目存取作業鏈結中的一個作業傳回 null,則鏈結的其餘部分不會執行。 在下列範例中,如果 B 被評估為 A,則 null 不會被評估;如果 CA 被評估為 B,則 null 不會被評估。

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

如果 A 可能為 Null,而 BC 不會為 Null(前提是 A 不是 Null),那麼您只需要對 A 使用 Null 條件運算子。

A?.B.C();

在上述範例中, B 不會評估 , C() 如果 A 為 null,則不會呼叫 。 不過,如果鏈結的成員存取遭到中斷,例如有括弧(如 (A?.B).C()),則不會發生短路。

下列範例示範 ?.?[] 運算符的使用方式:

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

第一個上述範例也會使用 null 聯合運算符 ?? 來指定替代表達式,以在 Null 條件運算的結果為 null的情況下進行評估。

如果 a.xa[x] 是不可為 Null 的值類型 T,那麼 a?.xa?[x] 就是對應的 可為 Null 的值類型T?。 如果您需要 類型的 T表達式,請將 null 聯合運算符 ?? 套用至 null 條件表達式,如下列範例所示:

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

在上述範例中,如果您不使用 ?? 運算符,當 numbers?.Length < 2false時,numbers 則評估為 null

備註

運算子 ?. 最多只會評估其左側運算元一次,保證在驗證為非 Null 後,不會變成 null

從 C# 14 開始,參考型別上允許使用 Null 條件式存取表示式(?.?[])進行指派運算。 例如,請參閱下列方法:

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

上述範例顯示對屬性和可能為 null 的參考型別上的索引元素的指派。 此作業的一個重要行為是,只有在已知左側為非空時,才會評估右側的 = 表達式。 例如,在下列程式代碼中,只有在陣列不是 Null 時,函式 GenerateNextIndex 才會被呼叫。 values如果陣列為 null,GenerateNextIndex則不會呼叫:

values?[2] = GenerateNextIndex();
int GenerateNextIndex() => index++;

換句話說,上述程式代碼相當於使用 if null 檢查語句的下列程式代碼:

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

除了賦值外,任何形式的 複合賦值,如 +=-=,都可以被允許。 不過,不允許遞增 (++) 和遞減 (--) 。

這項增強功能不會將 Null 條件表達式分類為變數。 它無法ref指派,也無法將它指派給ref變數,或以 或 ref 自變數的形式傳遞至方法out

安全線程委派調用

使用運算子 ?. 檢查代理是否為空,並以執行緒安全的方式(例如 在提出事件時)呼叫,如下程式碼所示:

PropertyChanged?.Invoke(…)

該程式代碼相當於下列程式代碼:

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

上述範例是一種執行緒安全的方式,可確保只叫用非空的handler。 因為委派實例是不可變的,所以沒有線程可以變更局部變數所 handler 參考的物件。 特別是,如果由另一個線程執行的程式碼取消訂閱PropertyChanged事件,並且在PropertyChanged被叫用之前null已變為handler時,該handler所參考的物件將保持不受影響。

呼叫表示式 ()

使用小括號 () 來調用 方法 或調用 委派

下列程式代碼示範如何呼叫方法、具有或不含自變數,以及叫用委派:

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

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

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

當你用運算子呼叫建構子new時,也會用括號。

() 的其他使用方式

您也可以使用括號來調整表達式中評估作業的順序。 如需詳細資訊,請參閱 C# 運算子

轉換表達式會執行明確的類型轉換,也會使用括號。

結束運算子 ^ 的索引

你可以用索引運算子和範圍運算子,類型是 可數的。 可型態有一個int名為 or LengthCount屬性,且有一個可存取get的 accessr。 集合表達式 也依賴 可計算 的類型。

備註

單一維度陣列 是可計算的。 多維陣列則不是。 你無法在多維陣列中使用 ^.. (距離)運算子。

^ 運算子會標示序列中某元素從結尾算起的位置。 針對長度為 length 的序列,^n 指向從序列開頭偏移 length - n 的元素。 例如, ^1 指向序列的最後一個專案,並 ^length 指向序列的第一個專案。

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

如上述範例所示,expression ^eSystem.Index 類型。 在運算式 ^e中,的結果 e 必須隱含轉換成 int

您也可以使用^搭配範圍運算子來建立索引範圍。 如需詳細資訊,請參閱 索引和範圍

從 C# 13 開始,你可以在物件初始化器中使用 end 運算 ^子的索引。

範圍運算子 ..

運算子 .. 會將索引範圍的開始和結尾指定為其作數。 左側作數是範圍的 內含 開始。 右側作數是範圍的 獨佔 結尾。 任一運算元都可以是序列起始點或結尾的索引,如下範例所示:

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

如上述範例所示,expression a..bSystem.Range 類型。 在表示式a..b中,和 a 的結果b必須隱含轉換成 Int32Index

這很重要

當值為負數時,從 隱含轉換 intIndex 擲回 ArgumentOutOfRangeException

您可以省略運算子的任何作數 .. ,以取得開放式範圍:

  • a.. 相當於 a..^0
  • ..b 相當於 0..b
  • .. 相當於 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));

下表顯示各種表達集合範圍的方式:

範圍運算子表達式 說明
.. 集合中的所有值。
..end 從開頭到 end 獨佔的值。
start.. start 內含到結尾的值。
start..end start 內含到 end 獨佔的值。
^start.. start 內含到結束計數的值。
..^end 從開頭到 end 從結尾進行獨佔計數的值。
start..^end start 內含到 end 從結尾獨佔計算的值。
^start..^end start 包含到 end 不包含的值,都從結尾計算。

下列範例示範使用上表所呈現之所有範圍的效果:

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

如需詳細資訊,請參閱 索引和範圍

標記 .. 也用於集合表示式中的 散佈專案

運算子多載性

你不能讓 、 ()^.. 運算子過載.[]操作員同時也是非過載操作員。 使用 索引器 來支援具有使用者定義型別的索引編製。

C# 語言規格

如需詳細資訊,請參閱 C# 語言規格的下列幾節:

如需索引和範圍的詳細資訊,請參閱 功能提案附註

另請參閱