Opérateurs et expressions d’accès aux membres (référence C#)

Vous pouvez utiliser les opérateurs et expressions suivants lorsque vous accédez à un membre de type :

  • (accès aux membres): pour accéder à un membre d’un espace de noms ou d’un type
  • (élément de tableau ou accès à l’indexeur): pour accéder à un élément de tableau ou à un indexeur de type
  • et ?[] (opérateurs conditionnels null): pour effectuer une opération d’accès de membre ou d’élément uniquement si un opérande n’est pas null
  • (appel): pour appeler une méthode accédée ou appeler un délégué
  • (index à partir de la fin): pour indiquer que la position de l’élément est à partir de la fin d’une séquence
  • (plage): pour spécifier une plage d’index que vous pouvez utiliser pour obtenir une plage d’éléments de séquence

Expression d’accès au membre.

Le jeton . sert à accéder à l’un des membres d’un espace de noms ou d’un type, comme le montrent les exemples suivants :

  • Utilisez . pour accéder à un espace de noms imbriqué dans un espace de noms, comme le montre l’exemple suivant d’une . :
using System.Collections.Generic;
  • Utilisez . pour former un . permettant d’accéder à un type dans un espace de noms, comme le montre le code suivant :
System.Collections.Generic.IEnumerable<int> numbers = new int[] { 1, 2, 3 };

Utilisez une directive pour rendre l’utilisation des noms qualifiés facultative.

  • Utilisez . pour accéder aux ., statiques et non statiques, comme le montre le code suivant :
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

Vous pouvez également utiliser . pour accéder à une ..

Opérateur d’indexeur []

Les crochets, [], sont généralement utilisés pour l’accès à un élément tableau, indexeur ou pointeur.

Accès aux tableaux

L’exemple suivant montre comment accéder à des éléments tableau :

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

Si un index de tableau est en dehors des limites de la dimension correspondante d’un tableau, une IndexOutOfRangeException est levée.

Comme le montre l’exemple précédent, vous utilisez également des crochets quand vous déclarez un type tableau ou instanciez une instance de tableau.

Pour plus d’informations sur les tableaux, consultez Tableaux.

Accès aux indexeurs

L’exemple suivant utilise le type .NET Dictionary<TKey,TValue> pour illustrer l’accès à l’indexeur :

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

Les indexeurs vous permettent d’indexer des instances d’un type défini par l’utilisateur en procédant de la même façon que pour l’indexation de tableau. Contrairement aux index de tableau, qui doivent être des entiers, les paramètres de l’indexeur peuvent être déclarés comme n’importe quel type.

Pour plus d’informations sur les indexeurs, consultez Indexeurs.

Autres utilisations de []

Pour plus d’informations concernant l’accès à l’élément de pointeur, consultez la section Opérateur d’accès à l’élément de pointeur [] de l’article Opérateurs associés au pointeur.

Vous utilisez également des crochets pour spécifier des attributs :

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

Opérateurs conditionnels Null ?. et ?[]

Disponible en C# 6 et versions ultérieures, un opérateur conditionnel null applique un accès de membre , ou d’accès à l' élément,, à son opérande uniquement si cet opérande a la valeur non NULL ; sinon, il retourne null . C'est

  • Si a prend la valeur null , le résultat de a?.x ou a?[x] est null .

  • Si a prend la valeur non null, le résultat de a?.x ou a?[x] est le même que le résultat de a.x ou a[x] , respectivement.

    Notes

    Si a.x ou a[x] lèvent une exception, a?.x ou a?[x] lèvent la même exception pour la valeur non null a . Par exemple, si a est une instance de tableau non null et x se trouve en dehors des limites de a , a?[x] lèvera un IndexOutOfRangeException .

Les opérateurs conditionnels Null ont un effet de court-circuit. Autrement dit, si une opération dans une chaîne d’opérations d’accès au membre ou à l’élément conditionnelles retourne une valeur null, le reste de la chaîne ne s’exécute pas. Dans l’exemple suivant, B n’est pas évalué si A prend la valeur null et C n’est pas évalué si A ou B prend la valeur null :

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

Si A peut avoir la valeur null, mais B et C qu’il ne s’agit pas de null si un n’est pas null, vous devez uniquement appliquer l’opérateur conditionnel null à A :

A?.B.C();

Dans l’exemple précédent, B n’est pas évalué et C() n’est pas appelé si A a la valeur null. Toutefois, si l’accès aux membres chaînés est interrompu, par exemple par des parenthèses comme dans (A?.B).C() , le court-circuit ne se produit pas.

Les exemples suivants illustrent l’utilisation des ?. opérateurs et ?[] :

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

Le premier des deux exemples précédents utilise également l' opérateur de fusion Null pour spécifier une autre expression à évaluer si le résultat d’une opération conditionnelle null est null .

Si a.x ou a[x] est un type T valeur n’acceptant pas les valeurs NULL, a?.x ou a?[x] est du a.xT? valeur Nullable correspondant. Si vous avez besoin d’une expression de type T , appliquez l’opérateur ?? de fusion Null à une expression conditionnelle null, comme le montre l’exemple suivant :

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

Dans l’exemple précédent, si vous n’utilisez pas l’opérateur, prend la ?? valeur false lorsque numbers est null . numbers?.Length < 2

L’opérateur d’accès aux membres conditionnels null ?. est également appelé l’opérateur Elvis.

Appel de délégué thread-safe

Utilisez l’opérateur ?. pour vérifier si un délégué est non Null et l’appeler de manière thread-safe (par exemple, quand vous ?.), comme illustré dans le code suivant :

PropertyChanged?.Invoke(…)

Ce code est équivalent au code suivant que vous utiliseriez dans C# 5 ou une version antérieure :

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

C’est une façon thread-safe de s’assurer que seule une valeur non null handler est appelée. Étant donné que les instances de délégué sont immuables, aucun thread ne peut modifier l’objet référencé par la handler variable locale. En particulier, si le code exécuté par un autre thread annule son abonnement à l' PropertyChanged événement et PropertyChanged devient null avant handler l’appel de, l’objet référencé par handler reste inchangé. L' ?. opérateur évalue son opérande de gauche un peu plus d' null une fois, ce qui garantit qu’il ne peut pas être modifié après avoir été vérifié comme non null.

Expression d’appel ()

Utilisez des parenthèses, (), pour appeler une () ou un délégué.

L’exemple suivant montre comment appeler une méthode, avec ou sans arguments, et un délégué :

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

Vous utilisez également des parenthèses quand vous appelez un constructeur avec l’opérateur .

Autres utilisations de ()

Vous utilisez également des parenthèses pour ajuster l’ordre dans lequel évaluer les opérations dans une expression. Pour plus d’informations, consultez Opérateurs C#.

Les expressions cast, qui effectuent des conversions de type explicites, utilisent aussi des parenthèses.

Index de fin d’opérateur ^

Disponible en C# 8,0 et versions ultérieures, l' ^ opérateur indique la position de l’élément à partir de la fin d’une séquence. Pour une séquence de longueur length , ^n pointe vers l’élément avec décalage length - n à partir du début d’une séquence. Par exemple, ^1 pointe vers le dernier élément d’une séquence et ^length pointe vers le premier élément d’une séquence.

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

Comme le montre l’exemple précédent, expression ^e est du System.Index type. Dans Expression ^e , le résultat de e doit être implicitement convertible en int .

Vous pouvez également utiliser l' ^ opérateur avec l' ^ pour créer une plage d’index. Pour plus d’informations, consultez index et plages.

Opérateur de plage..

Disponible en C# 8,0 et versions ultérieures, l' .. opérateur spécifie le début et la fin d’une plage d’index comme opérandes. L’opérande de gauche est un début inclusif d’une plage. L’opérande de droite est une extrémité exclusive d’une plage. L’un ou l’autre des opérandes peut être un index à partir du début ou de la fin d’une séquence, comme le montre l’exemple suivant :

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

Comme le montre l’exemple précédent, expression a..b est du System.Range type. Dans Expression a..b , les résultats de a et b doivent être implicitement convertibles en int ou Index .

Vous pouvez omettre l’un des opérandes de l' .. opérateur pour obtenir une plage ouverte :

  • a.. équivaut à a..^0
  • ..b équivaut à 0..b
  • .. équivaut à 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));

Pour plus d’informations, consultez index et plages.

Capacité de surcharge de l’opérateur

Les . opérateurs, ^() , et .. ne peuvent pas être surchargés. L’opérateur [] est également considéré comme un opérateur non surchargeable. Utilisez des indexeurs pour prendre en charge l’indexation avec des types définis par l’utilisateur.

spécification du langage C#

Pour plus d’informations, consultez les sections suivantes de la spécification du langage C# :

Pour plus d’informations sur les index et les plages, consultez la Remarque relativeà la proposition de fonctionnalité.

Voir aussi