Types référence intégrés (Référence C#)

C# a de nombreux types référence intégrés. Ils ont des mots clés ou des opérateurs qui sont synonymes pour un type dans la bibliothèque .NET.

Type d’objet.

Le type object est un alias de System.Object dans .NET. Dans le système de type unifié de C#, tous les types (les types référence et valeur, prédéfinis ou définis par l’utilisateur) héritent directement ou indirectement du type System.Object. Vous pouvez assigner des valeurs de tout type aux variables de type object. Toute variable object peut être attribuée à sa valeur par défaut à l’aide du littéral null. Quand une variable d’un type valeur est convertie en type objet, elle est dite boxed. Quand une variable de type object est convertie en type valeur, elle est dite unboxed. Pour plus d’informations, consultez Conversion boxing et unboxing.

Type de chaîne

Le type string représente une séquence de zéro, un ou plusieurs caractères Unicode. string est un alias de System.String dans .NET.

Bien que string soit un type référence, les opérateurs d’égalité== (!= et ) sont définis pour comparer les valeurs des objets string, pas les références. L’égalité basée sur les valeurs permet de tester l’égalité de chaînes de façon plus intuitive. Par exemple :

string a = "hello";
string b = "h";
// Append to contents of 'b'
b += "ello";
Console.WriteLine(a == b);
Console.WriteLine(object.ReferenceEquals(a, b));

Cet exemple précédent affiche « True », puis « False », car le contenu des chaînes est équivalent, mais a et b ne font pas référence à la même instance de chaîne.

L’opérateur + concatène les chaînes :

string a = "good " + "morning";

Le code précédent crée un objet String qui contient « good morning ».

Les chaînes sont immuables : le contenu d’un objet de chaîne ne peut pas être modifié une fois l’objet créé. Par exemple, lorsque vous écrivez ce code, le compilateur crée en fait un nouvel objet String pour stocker la nouvelle séquence de caractères, et ce nouvel objet est attribué à b. La mémoire allouée pour b (quand elle contenait la chaîne « h ») est alors éligible pour le garbage collection.

string b = "h";
b += "ello";

L’[]opérateurpeut être utilisé pour un accès en lecture seule aux différents caractères d’une chaîne. Les valeurs index valides commencent à 0 et doivent être inférieures à la longueur de la chaîne :

string str = "test";
char x = str[2];  // x = 's';

De la même façon, l’opérateur [] permet également d’itérer sur chaque caractère dans une chaîne :

string str = "test";

for (int i = 0; i < str.Length; i++)
{
  Console.Write(str[i] + " ");
}
// Output: t e s t

Littéraux de chaîne

Les littéraux de chaîne sont de type string et peuvent être écrits sous trois formes, brut, entre guillemets et verbatim.

Les littéraux de chaîne brute sont disponibles à partir de C# 11. Les littéraux de chaîne brute peuvent contenir du texte arbitraire sans nécessiter de séquences d’échappement. Les littéraux de chaîne brute peuvent inclure des espaces blancs et de nouvelles lignes, des guillemets incorporés et d’autres caractères spéciaux. Les littéraux de chaîne brutes sont placés entre trois guillemets doubles (""") au minimum :

"""
This is a multi-line
    string literal with the second line indented.
"""

Vous pouvez même inclure une séquence de trois (ou plus) guillemets doubles. Si votre texte nécessite une séquence incorporée de guillemets, vous commencez et terminez le littéral de chaîne brute avec plus de guillemets, selon les besoins :

"""""
This raw string literal has four """", count them: """" four!
embedded quote characters in a sequence. That's why it starts and ends
with five double quotes.

You could extend this example with as many embedded quotes as needed for your text.
"""""

Les littéraux de chaîne brute ont généralement les séquences de guillemets de début et de fin sur des lignes distinctes du texte incorporé. Les littéraux de chaîne brute multiligne prennent en charge les chaînes qui sont elles-mêmes des chaînes entre guillemets :

var message = """
"This is a very important message."
""";
Console.WriteLine(message);
// output: "This is a very important message."

Lorsque les guillemets de début et de fin se trouvent sur des lignes distinctes, les nouvelles lignes qui suivent le guillemet d’ouverture et qui précèdent le guillemet de fin ne sont pas incluses dans le contenu final. La séquence de guillemets de fermeture dicte la colonne la plus à gauche pour le littéral de chaîne. Vous pouvez mettre en retrait un littéral de chaîne brute pour qu’il corresponde au format de code global :

var message = """
    "This is a very important message."
    """;
Console.WriteLine(message);
// output: "This is a very important message."
// The leftmost whitespace is not part of the raw string literal

Les colonnes à droite de la séquence de guillemets de fin sont conservées. Ce comportement active les chaînes brutes pour les formats de données tels que JSON, YAML ou XML, comme illustré dans l’exemple suivant :

var json= """
    {
        "prop": 0
    }
    """;

Le compilateur émet une erreur si l’une des lignes de texte s’étend à gauche de la séquence de guillemets de fermeture. Les séquences de guillemets d’ouverture et de fermeture peuvent se trouver sur la même ligne, à condition que le littéral de chaîne ne commence ni ne se termine par un caractère de guillemet :

var shortText = """He said "hello!" this morning.""";

Vous pouvez combiner des littéraux de chaîne bruts avec l’interpolation de chaîne pour inclure des guillemets et des accolades dans la chaîne de sortie.

Les littéraux de chaîne entre guillemets sont placés entre guillemets doubles (") :

"good morning"  // a string literal

Les littéraux de chaîne peuvent contenir tout littéral de caractère. Les séquences d’échappement sont incluses. L’exemple suivant utilise la séquence d’échappement \\ pour la barre oblique inverse, \u0066 pour la lettre f et \n pour un saut de ligne.

string a = "\\\u0066\n F";
Console.WriteLine(a);
// Output:
// \f
//  F

Notes

Le code d’échappement \udddd (où dddd est un nombre à quatre chiffres) représente le caractère Unicode U+dddd. Les codes d’échappement Unicode à huit chiffres sont également reconnus : \Udddddddd.

Les littéraux de chaîne textuelle commencent par @ et sont placés entre guillemets doubles. Par exemple :

@"good morning"  // a string literal

L’avantage des chaînes textuelles est que les séquences d’échappement ne sont pas traitées, ce qui facilite l’écriture. Par exemple, le texte suivant correspond à un nom de fichier Windows complet :

@"c:\Docs\Source\a.txt"  // rather than "c:\\Docs\\Source\\a.txt"

Pour inclure un guillemet double dans une chaîne @-quoted, doublez-le :

@"""Ahoy!"" cried the captain." // "Ahoy!" cried the captain.

Littéraux de chaîne UTF-8

Les chaînes dans .NET sont stockées à l’aide de l’encodage UTF-16. UTF-8 est la norme pour les protocoles Web et d’autres bibliothèques importantes. À compter de C# 11, vous pouvez ajouter le suffixe u8 à un littéral de chaîne pour spécifier l’encodage UTF-8. Les littéraux UTF-8 sont stockés en tant qu’objets ReadOnlySpan<byte>. Le type naturel d’un littéral de chaîne UTF-8 est ReadOnlySpan<byte>. L’utilisation d’un littéral de chaîne UTF-8 crée une déclaration plus claire que la déclaration de l’équivalent System.ReadOnlySpan<T>, comme indiqué dans le code suivant :

ReadOnlySpan<byte> AuthWithTrailingSpace = new byte[] { 0x41, 0x55, 0x54, 0x48, 0x20 };
ReadOnlySpan<byte> AuthStringLiteral = "AUTH "u8;

Pour stocker un littéral de chaîne UTF-8 en tant que tableau, vous devez utiliser ReadOnlySpan<T>.ToArray() pour copier les octets contenant le littéral dans le tableau mutable :

byte[] AuthStringLiteral = "AUTH "u8.ToArray();

Les littéraux de chaîne UTF-8 ne sont pas des constantes au moment de la compilation ; ce sont des constantes d’exécution. Par conséquent, ils ne peuvent pas être utilisés comme valeur par défaut pour un paramètre facultatif. Les littéraux de chaîne UTF-8 ne peuvent pas être combinés avec l’interpolation de chaîne. Vous ne pouvez pas utiliser le jeton $ et le suffixe u8 sur la même expression de chaîne.

Type de délégué

La déclaration d’un type délégué est semblable à une signature de méthode. Elle a une valeur de retour et un nombre quelconque de paramètres de type quelconque :

public delegate void MessageDelegate(string message);
public delegate int AnotherDelegate(MyType m, long num);

Dans .NET, les types System.Action et System.Func fournissent des définitions génériques pour de nombreux délégués courants. Vous n’avez probablement pas besoin de définir de nouveaux types de délégué personnalisés. À la place, vous pouvez créer des instanciations des types génériques fournis.

Un delegate est un type référence qui peut être utilisé pour encapsuler une méthode anonyme ou nommée. Les délégués sont comparables aux pointeurs fonction en C++, mais ils offrent l’avantage d’être sûrs et de type sécurisé. Pour les applications de délégués, consultez Délégués et Délégués génériques. Les délégués sont la base des événements. Un délégué peut être instancié en l’associant à une méthode nommée ou anonyme.

Le délégué doit être instancié avec une méthode ou une expression lambda qui a un type de retour compatible et des paramètres d’entrée. Pour plus d’informations sur le degré de variance autorisé dans la signature de méthode, consultez Variance dans les délégués. Pour une utilisation avec des méthodes anonymes, le délégué et le code à lui associer sont déclarés ensemble.

La combinaison ou la suppression de délégués échoue avec une exception d’exécution lorsque les types de délégués impliqués au moment de l’exécution sont différents en raison de la conversion de variantes. L'exemple suivant illustre une situation qui peut échouer :

Action<string> stringAction = str => {};
Action<object> objectAction = obj => {};
  
// Valid due to implicit reference conversion of
// objectAction to Action<string>, but may fail
// at run time.
Action<string> combination = stringAction + objectAction;

Vous pouvez créer un délégué avec le type d’exécution correct en créant un nouvel objet délégué. L’exemple suivant montre comment cette solution de contournement peut être appliquée à l’exemple précédent.

Action<string> stringAction = str => {};
Action<object> objectAction = obj => {};
  
// Creates a new delegate instance with a runtime type of Action<string>.
Action<string> wrappedObjectAction = new Action<string>(objectAction);

// The two Action<string> delegate instances can now be combined.
Action<string> combination = stringAction + wrappedObjectAction;

Vous pouvez déclarer des pointeurs de fonction, qui utilisent une syntaxe similaire. Un pointeur de fonction utilise l’instruction calli au lieu d’instancier un type délégué et d’appeler la méthode virtuelle Invoke.

Type dynamique

Le type dynamic indique que l’utilisation de la variable et des références à ses membres contourne la vérification du type au moment de la compilation. Au lieu de cela, ces opérations sont résolues au moment de l’exécution. Le type dynamic simplifie l’accès aux API COM telles que les API Office Automation, aux API dynamiques telles que les bibliothèques IronPython et au modèle DOM (Document Object Model) HTML.

Le type dynamic se comporte comme le type object dans la plupart des cas. En particulier, toute expression non null peut être convertie en type dynamic. Le type dynamic diffère de object en cela que les opérations qui contiennent des expressions de type dynamic ne sont pas résolues et leur type n’est pas vérifié par le compilateur. Le compilateur empaquète des informations sur l’opération, qui sont ensuite utilisées pour évaluer l’opération au moment de l’exécution. Dans le cadre du processus, les variables de type dynamic sont compilées dans des variables de type object. Ainsi, le type dynamic existe seulement au moment de la compilation, et non au moment de l’exécution.

L’exemple suivant compare une variable de type dynamic à une variable de type object. Pour vérifier le type de chaque variable au moment de la compilation, placez le pointeur de la souris sur dyn ou obj dans les instructions WriteLine. Copiez le code suivant dans un éditeur où IntelliSense est disponible. IntelliSense affiche dynamic pour dyn et object pour obj.

class Program
{
    static void Main(string[] args)
    {
        dynamic dyn = 1;
        object obj = 1;

        // Rest the mouse pointer over dyn and obj to see their
        // types at compile time.
        System.Console.WriteLine(dyn.GetType());
        System.Console.WriteLine(obj.GetType());
    }
}

Les instructions WriteLine affichent les types d’exécution de dyn et obj. À ce stade, tous deux ont le même type, entier. La sortie suivante est produite :

System.Int32
System.Int32

Pour voir la différence entre dyn et obj au moment de la compilation, ajoutez les deux lignes suivantes entre les déclarations et les instructions WriteLine de l’exemple précédent.

dyn = dyn + 3;
obj = obj + 3;

Une erreur du compilateur est signalée pour la tentative d’ajout d’un entier et un d’objet dans l’expression obj + 3. Toutefois, aucune erreur n’est signalée pour dyn + 3. L’expression qui contient dyn n’est pas vérifié au moment de la compilation, car le type de dyn est dynamic.

L’exemple suivant utilise dynamic dans plusieurs déclarations. La méthode Main compare également la vérification de type au moment de la compilation avec la vérification de type au moment de l’exécution.

using System;

namespace DynamicExamples
{
    class Program
    {
        static void Main(string[] args)
        {
            ExampleClass ec = new ExampleClass();
            Console.WriteLine(ec.ExampleMethod(10));
            Console.WriteLine(ec.ExampleMethod("value"));

            // The following line causes a compiler error because ExampleMethod
            // takes only one argument.
            //Console.WriteLine(ec.ExampleMethod(10, 4));

            dynamic dynamic_ec = new ExampleClass();
            Console.WriteLine(dynamic_ec.ExampleMethod(10));

            // Because dynamic_ec is dynamic, the following call to ExampleMethod
            // with two arguments does not produce an error at compile time.
            // However, it does cause a run-time error.
            //Console.WriteLine(dynamic_ec.ExampleMethod(10, 4));
        }
    }

    class ExampleClass
    {
        static dynamic _field;
        dynamic Prop { get; set; }

        public dynamic ExampleMethod(dynamic d)
        {
            dynamic local = "Local variable";
            int two = 2;

            if (d is int)
            {
                return local;
            }
            else
            {
                return two;
            }
        }
    }
}
// Results:
// Local variable
// 2
// Local variable

spécification du langage C#

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

Voir aussi