メンバー アクセス演算子と式 (C# リファレンス)

型のメンバーにアクセスするときは、次の演算子と式を使用できます。

メンバー アクセス式 .

以下の例に示すように、名前空間のメンバーまたは型にアクセスするために . トークンを使います。

  • 次の using ディレクティブの例に示すように、. を使って、名前空間内の入れ子になった名前空間にアクセスします。
using System.Collections.Generic;
  • 次のコードに示すように、. を使って "修飾名" を作成して名前空間内の型にアクセスします。
System.Collections.Generic.IEnumerable<int> numbers = new int[] { 1, 2, 3 };

using ディレクティブを使い、必要に応じて修飾名を利用します。

  • 次のコードに示すように、. を使って、型のメンバー (静的および非静的) にアクセスします。
var constants = new List<double>();
constants.Add(Math.PI);
constants.Add(Math.E);
Console.WriteLine($"{constants.Count} values to show:");
Console.WriteLine(string.Join(", ", constants));
// Output:
// 2 values to show:
// 3.14159265358979, 2.71828182845905

また、. を使って拡張メソッドにアクセスすることもできます。

インデクサー演算子 []

通常、角かっこ [] は、配列、インデクサー、またはポインター要素へのアクセスに使用されます。

配列へのアクセス

次の例は、配列要素へのアクセス方法を示しています。

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

インデクサーを使用すると、配列のインデックス作成と同様の方法でユーザー定義型のインスタンスのインデックスを作成することができます。 整数である必要がある配列インデックスとは異なり、任意の型を持つインデクサー パラメーターを宣言できます。

インデクサーの詳細については、「インデクサー」を参照してください。

[] の他の使用方法

ポインター要素へのアクセスの詳細については、ポインターに関連する演算子に関する記事の「ポインター要素アクセス演算子 []」セクションを参照してください。

角かっこは、属性を指定するためにも使用されます。

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

Null 条件演算子 ?. および ?[]

C# 6 以降で使用できる Null 条件付き演算子は、そのオペランドが null 以外と評価された場合にのみ、オペランドにメンバー アクセス操作 (?.) または要素アクセス操作 (?[]) を適用し、それ以外の場合は、null を返します。 つまり、以下のようになります。

  • anull と評価された場合、a?.x または a?[x] の結果は null です。

  • a が null 以外と評価された場合、a?.x または a?[x] の結果は、a.x または a[x] の結果とそれぞれ同じです。

    注意

    a.x または a[x] が例外をスローした場合は、a?.x または a?[x] が、null 以外の a に対して同じ例外をスローします。 たとえば、a が null 以外の配列インスタンスで、xaの範囲外にある場合、a?[x]IndexOutOfRangeException をスローします。

Null 条件演算子はショートサーキットです。 つまり、条件付きのメンバーまたは要素アクセス操作のチェーン内にある 1 つの操作から null が返された場合、残りのチェーンは実行されません。 次の例では、Anull と評価されると B は評価されず、A または Bnull と評価されると C は評価されません。

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

A は null になる可能性があるが、A が null でない場合に B および C が null にならない場合は、null 条件演算子を A に適用するだけで済みます。

A?.B.C();

前の例では、A が null の場合、B は評価されず、C() は呼び出されません。 ただし、チェーン メンバーのアクセスが (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

var numberSets = new List<double[]>
{
    new[] { 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
using System;
using System.Collections.Generic;
using System.Linq;

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 FullName Name { get; set; }
    }

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

前の 2 つの例の 1 番目では、null 条件演算の結果が null の場合に評価する代替の式を指定するために、null 合体演算子 ?? も使用しています。

a.x または a[x] が null 非許容値型の T の場合は、a?.x または a?[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(new int[0]));  // output: 0
Console.WriteLine(GetSumOfFirstTwoOrDefault(new[] { 3, 4, 5 }));  // output: 7

前の例では、?? 演算子を使用しなければ、numbersnull の場合、numbers?.Length < 2false と評価されます。

Null 条件メンバー アクセス演算子 ?. は Elvis 演算子とも呼ばれます。

スレッドセーフなデリゲートの呼び出し

次のコードに示すように、?. 演算子を使用してデリゲートが null 以外かどうかを確認し、それをスレッドセーフな方法で呼び出します (たとえば、イベントを発生させる場合)。

PropertyChanged?.Invoke(…)

このコードは、C# 5 以前で使用する次のコードと同等です。

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

これは、null 以外の handler のみが呼び出されるようにするためのスレッドセーフな方法です。 デリゲート インスタンスは不変であるため、handler ローカル変数によって参照されるオブジェクトを変更できるスレッドはありません。 具体的には、別のスレッドによって実行されるコードが PropertyChanged イベントから登録解除され、handler が呼び出される前に PropertyChangednull になる場合、handler によって参照されるオブジェクトは影響を受けません。 ?. 演算子では、左側のオペランドが 1 回だけ評価され、null 以外として検証された後に null に変更できないことが保証されます。

呼び出し式 ()

かっこ () は、メソッドを呼び出すとき、またはデリゲートを呼び出すときに使用します。

メソッドを呼び出す方法 (引数を指定した場合と指定しない場合) とデリゲートを呼び出す方法の例を次に示します。

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

var numbers = new List<int>();
numbers.Add(10);
numbers.Add(17);
display(numbers.Count);   // output: 2

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

かっこは、new 演算子を使用してコンストラクターを呼び出すときにも使用します。

() の他の使用方法

式に含まれる演算を評価する順序を調整する場合にもかっこを使用します。 詳細については、C# 演算子に関するページを参照してください。

明示的な型変換を実行するキャスト式でも、かっこが使われます。

末尾からのインデックス演算子 ^

^ 演算子は C# 8.0 以降で使用することができ、要素の位置がシーケンスの末尾からであることを示します。 長さが length のシーケンスの場合、^n は、シーケンスの先頭からのオフセットが length - n である要素を指します。 たとえば、^1 は、シーケンスの最後の要素を指し、^length は、シーケンスの最初の要素を指します。

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

var lines = new List<string> { "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

前の例で示すように、式 ^eSystem.Index 型です。 式 ^eで、e の結果は int に暗黙に変換される必要があります。

さらに、^ 演算子を範囲演算子 と組み合わせて使用してインデックスの範囲を作成することもできます。 詳細については、「インデックスと範囲」を参照してください。

範囲演算子 .

.. 演算子は C# 8.0 以降で使用することができ、インデックスの範囲の先頭と末尾をオペランドとして指定します。 左側のオペランドは "包含的" で、範囲の先頭を含みます。 右側のオペランドは "排他的" で、範囲の末尾を含みません。 次の例で示すように、どちらのオペランドであっても、シーケンスの先頭または末尾からのインデックスとすることができます。

int[] numbers = new[] { 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));

前の例で示すように、式 a..bSystem.Range 型です。 式 a..b で、a および b の結果は暗黙に int または Index に変換される必要があります。

.. 演算子のオペランドのいずれかを省略して、変更可能な範囲を取得することができます。

  • a..a..^0 と同じです。
  • ..b0..b と同じです。
  • ..0..^0 と同じです。
int[] numbers = new[] { 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));

詳細については、「インデックスと範囲」を参照してください。

演算子のオーバーロード可/不可

.()^、および .. の各演算子はオーバーロードできません。 [] 演算子も、オーバーロードできない演算子と見なされます。 ユーザー定義型を使用したインデックス作成をサポートするには、インデクサーを使用してください。

C# 言語仕様

詳細については、「C# 言語仕様」の次のセクションを参照してください。

インデックスと範囲について詳しくは、機能提案メモを参照してください。

関連項目