Partager via


10 conversions

10.1 Général

Une conversion entraîne la conversion d’une expression vers, ou traitée comme étant d’un type particulier ; dans l’ancien cas, une conversion peut impliquer un changement dans la représentation. Les conversions peuvent être implicites ou explicites, ce qui détermine si un cast explicite est requis.

Exemple : par exemple, la conversion du type int en type long est implicite, de sorte que les expressions de type int peuvent implicitement être traitées comme du type long. La conversion opposée, du type au type longint, est explicite et donc un cast explicite est requis.

int a = 123;
long b = a;      // implicit conversion from int to long
int c = (int) b; // explicit conversion from long to int

exemple de fin

Certaines conversions sont définies par la langue. Les programmes peuvent également définir leurs propres conversions (§10.5).

Certaines conversions dans le langage sont définies à partir d’expressions en types, d’autres de types à types. Une conversion d’un type s’applique à toutes les expressions qui ont ce type.

Exemple :

enum Color { Red, Blue, Green }

// The expression 0 converts implicitly to enum types
Color c0 = 0;

// Other int expressions need explicit conversion
Color c1 = (Color)1;

// Conversion from null expression (no type) to string
string x = null;

// Conversion from lambda expression to delegate type
Func<int, int> square = x => x * x;

exemple de fin

10.2 Conversions implicites

10.2.1 Général

Les conversions suivantes sont classées comme conversions implicites :

  • Conversions d’identités (§10.2.2)
  • Conversions numériques implicites (§10.2.3)
  • Conversions d’énumération implicites (§10.2.4)
  • Conversions de chaînes interpolées implicites (§10.2.5)
  • Conversions de référence implicites (§10.2.8)
  • Conversions de boxe (§10.2.9)
  • Conversions dynamiques implicites (§10.2.10)
  • Conversions implicites de paramètres de type (§10.2.12)
  • Conversions d’expressions constantes implicites (§10.2.11)
  • Conversions implicites définies par l’utilisateur (y compris les conversions levées) (§10.2.14)
  • Conversions de fonctions anonymes (§10.2.15)
  • Conversions de groupes de méthodes (§10.2.15)
  • Conversions littérales Null (§10.2.7)
  • Conversions nullables implicites (§10.2.6)
  • Conversions de tuple implicites (§10.2.13)
  • Conversions littérales par défaut (§10.2.16)
  • Conversions de levée implicites (§10.2.17)

Les conversions implicites peuvent se produire dans diverses situations, notamment les appels des membres de fonction (§12.6.6),les expressions de cast (§12.9.7) et les affectations (§12.21).

Les conversions implicites prédéfinies réussissent toujours et ne provoquent jamais la levée d’exceptions.

Remarque : Les conversions implicites définies par l’utilisateur correctement conçues doivent également présenter ces caractéristiques. Note de fin

Pour les besoins de la conversion, les types object et dynamic sont convertibles en identité (§10.2.2).

Toutefois, les conversions dynamiques (§10.2.10) s’appliquent uniquement aux expressions de type dynamic (§8.2.4).

10.2.2 Conversion d’identité

Une conversion d’identité convertit de n’importe quel type vers le même type ou un type équivalent au moment de l’exécution. Une raison pour laquelle cette conversion existe est de sorte qu’un type T ou une expression de type T peut être considéré comme convertible T en lui-même. Les conversions d’identité suivantes existent :

  • Entre T et T, pour n’importe quel type T.
  • Entre T et T? pour n’importe quel type Tde référence .
  • Entre object et dynamic.
  • Entre tous les types de tuples avec la même arité et le type construit ValueTuple<...> correspondant, lorsqu’une conversion d’identité existe entre chaque paire de types d’éléments correspondants.
  • Entre les types construits à partir du même type générique où il existe une conversion d’identité entre chaque argument de type correspondant.

Exemple : L’exemple suivant illustre la nature récursive de la troisième règle :

(int a , string b) t1 = (1, "two");
(int c, string d) t2 = (3, "four");

// Identity conversions exist between
// the types of t1, t2, and t3.
var t3 = (5, "six");
t3 = t2;
t2 = t1;

var t4 = (t1, 7);
var t5 = (t2, 8);

// Identity conversions exist between
// the types of t4, t5, and t6.
var t6 =((8, "eight"), 9);
t6 = t5;
t5 = t4;

Les types de tuples t1et t2t3 tous ont deux éléments : un int suivi d’un string. Les types d’éléments Tuple peuvent eux-mêmes par des tuples, comme dans t4, t5et t6. Une conversion d’identité existe entre chaque paire de types d’éléments correspondants, y compris les tuples imbriqués, donc une conversion d’identité existe entre les types de tuples t4, t5et t6.

exemple de fin

Toutes les conversions d’identité sont symétriques. Si une conversion d’identité existe depuis T₁ vers , une conversion d’identité existe à partir de T₂T₂.T₁ Deux types sont convertibles d’identité lorsqu’une conversion d’identité existe entre deux types.

Dans la plupart des cas, une conversion d’identité n’a aucun effet au moment de l’exécution. Toutefois, étant donné que les opérations à virgule flottante peuvent être effectuées à une précision supérieure à celle prescrite par leur type (§8.3.7), l’affectation de leurs résultats peut entraîner une perte de précision et des casts explicites sont garantis pour réduire la précision à ce qui est prescrit par le type (§12.9.7).

10.2.3 Conversions numériques implicites

Les conversions numériques implicites sont les suivantes :

  • De sbyte à short, , int, longfloat, , double, ou decimal.
  • De byte à short, ushortintuintlong, ulong, float, , double, ou .decimal
  • De short à int, , long, float, double, ou decimal.
  • De ushort à int, , uint, longulong, , float, double, ou decimal.
  • De int à long, float, , doubleou decimal.
  • De uint à long, , ulong, float, double, ou decimal.
  • De long à float, doubleou decimal.
  • De ulong à float, doubleou decimal.
  • De char à ushort, , int, uintlong, ulong, , float, double, ou decimal.
  • De float à double.

Les conversions de int, vers uintlonget ulong vers floatlong ou vers ou ulong vers double peuvent entraîner une perte de précision, mais ne provoque jamais une perte de grandeur. Les autres conversions numériques implicites ne perdent jamais d’informations.

Il n’existe pas de conversions implicites prédéfinies vers le char type. Les valeurs des autres types intégraux ne sont donc pas converties automatiquement en char type.

10.2.4 Conversions d’énumération implicites

Une conversion d’énumération implicite permet une constant_expression (§12.23) avec n’importe quel type entier et la valeur zéro à convertir en enum_type et en tout nullable_value_type dont le type sous-jacent est un enum_type. Dans ce dernier cas, la conversion est évaluée en convertissant en enum_type sous-jacente et en encapsulant le résultat (§8.3.12).

10.2.5 Conversions de chaînes interpolées implicites

Une conversion de chaîne interpolée implicite permet à un interpolated_string_expression (§12.8.3System.FormattableStringSystem.IFormattable Lorsque cette conversion est appliquée, une valeur de chaîne n’est pas composée à partir de la chaîne interpolée. Au lieu de cela, une instance est System.FormattableString créée, comme décrit plus loin dans le §12.8.3.

10.2.6 Conversions nullables implicites

Les conversions nullables implicites sont ces conversions nullables (§10.6.1) dérivées des conversions prédéfinies implicites.

10.2.7 Conversions littérales Null

Une conversion implicite existe du null littéral vers n’importe quel type de référence ou type valeur nullable. Cette conversion produit une référence Null si le type cible est un type référence ou la valeur Null (§8.3.12) du type de valeur Nullable donné.

Conversions de référence implicites 10.2.8

Les conversions de référence implicites sont les suivantes :

  • De n’importe quel reference_type à object et dynamic.
  • De n’importe quel class_type à n’importe quel S, fourni T est dérivé de S.T
  • De n’importe quel class_type à n’importe T
  • De n’importe quel interface_type à n’importe quel interface_type S , fourni T est dérivé de S.T
  • D’un S avec un type Sᵢ d’élément à un T avec un type Tᵢd’élément , à condition que toutes les valeurs suivantes soient remplies :
    • S et T diffèrent uniquement dans le type d’élément. En d’autres termes, S et T ont le même nombre de dimensions.
    • Une conversion de référence implicite existe de Sᵢ vers Tᵢ.
  • D’un type S[] de tableau unidimensionnel vers System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyList<T>et leurs interfaces de base, à condition qu’il existe une conversion implicite d’identité ou de référence de S vers T.
  • Depuis n’importe quel array_type vers System.Array et les interfaces qu’il implémente.
  • Depuis n’importe quel delegate_type vers System.Delegate et les interfaces qu’il implémente.
  • Du littéral Null (§6.4.5.7) à n’importe quel type de référence.
  • De n’importe quel reference_type à un T s’il a une identité implicite ou une conversion de référence vers un reference_typeT₀ et T₀ a une conversion d’identité en T.
  • De n’importe quel T .
  • Conversions implicites impliquant des paramètres de type connus pour être des types de référence. Pour plus d’informations sur les conversions implicites impliquant des paramètres de type, consultez le §10.2.12 .

Les conversions de référence implicites sont ces conversions entre les reference_types qui peuvent être prouvées pour toujours réussir, et nécessitent donc aucune vérification au moment de l’exécution.

Les conversions de référence, implicites ou explicites, ne modifient jamais l’identité référentielle de l’objet en cours de conversion.

Remarque : En d’autres termes, lorsqu’une conversion de référence peut modifier le type de la référence, elle ne modifie jamais le type ou la valeur de l’objet référencé. Note de fin

Conversions de boxe 10.2.9

Une conversion de boxe permet à une value_type d’être convertie implicitement en reference_type. Les conversions de boxe suivantes existent :

  • De n’importe quel value_type au type object.
  • De n’importe quel value_type au type System.ValueType.
  • De n’importe quel enum_type au type System.Enum.
  • De n’importe quel non_nullable_value_type à n’importe quelle interface_type implémentée par le non_nullable_value_type.
  • De n’importe quel non_nullable_value_type à n’importe I
  • De n’importe quel non_nullable_value_type à n’importe I
  • De n’importe quel nullable_value_type à n’importe quel reference_type où il existe une conversion de boxing du type sous-jacent du nullable_value_type vers le reference_type.
  • À partir d’un paramètre de type qui n’est pas connu pour être un type référence à un type tel que la conversion est autorisée par §10.2.12.

La boxe une valeur d’un type valeur non nullable consiste à allouer une instance d’objet et à copier la valeur dans cette instance.

La boxe d’une valeur d’un nullable_value_type produit une référence Null s’il s’agit de la valeur Null (HasValue est false) ou du résultat d’annulation et de boxing de la valeur sous-jacente dans le cas contraire.

Remarque : Le processus de boxe peut être imaginé en termes d’existence d’une classe boxing pour chaque type valeur. Par exemple, envisagez d’implémenter une struct S interface I, avec une classe boxing appelée S_Boxing.

interface I
{
    void M();
}

struct S : I
{
    public void M() { ... }
}

sealed class S_Boxing : I
{
    S value;

    public S_Boxing(S value)
    {
        this.value = value;
    }

    public void M()
    {
        value.M();
    }
}

La boxe d’une valeur v de type S consiste désormais à exécuter l’expression new S_Boxing(v) et à renvoyer l’instance résultante en tant que valeur du type cible de la conversion. Ainsi, les instructions

S s = new S();
object box = s;

peut être considéré comme similaire à :

S s = new S();
object box = new S_Boxing(s);

Le type de boxe imaginé décrit ci-dessus n’existe pas réellement. Au lieu de cela, une valeur boxée de type S a le type Sd’exécution et une vérification de type runtime à l’aide de l’opérateur is avec un type valeur comme opérande droit teste si l’opérande gauche est une version boxée de l’opérande droit. Par exemple,

int i = 123;
object box = i;
if (box is int)
{
    Console.Write("Box contains an int");
}

génère les éléments suivants :

Box contains an int

Une conversion de boxe implique d’effectuer une copie de la valeur en cours de boxe. Il s’agit d’une conversion d’une reference_type en type object, dans laquelle la valeur continue de référencer la même instance et est simplement considérée comme le type objectmoins dérivé. Par exemple, les éléments suivants

struct Point
{
    public int x, y;

    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

class A
{
    void M() 
    {
        Point p = new Point(10, 10);
        object box = p;
        p.x = 20;
        Console.Write(((Point)box).x);
    }
}

génère la valeur 10 sur la console, car l’opération de boxe implicite qui se produit dans l’affectation des pbox causes de la copie de la valeur.p Au lieu Point de cela, la valeur 20 était déclarée class , car p elle box référencerait la même instance.

L’analogie d’une classe boxe ne doit pas être utilisée comme outil plus qu’un outil utile pour picturer le fonctionnement conceptuel de boxe. Il existe de nombreuses différences subtiles entre le comportement décrit par cette spécification et le comportement qui résulterait de l’implémentation de boxe de manière précise.

Note de fin

10.2.10 Conversions dynamiques implicites

Une conversion dynamique implicite existe d’une expression de type dynamique en n’importe quel type T. La conversion est liée dynamiquement au §12.3.3, ce qui signifie qu’une conversion implicite sera recherchée au moment de l’exécution du type d’exécution de l’expression vers T. Si aucune conversion n’est trouvée, une exception d’exécution est levée.

Cette conversion implicite enfreint apparemment les conseils au début du §10.2 qu’une conversion implicite ne doit jamais provoquer d’exception. Toutefois, ce n’est pas la conversion elle-même, mais la recherche de la conversion qui provoque l’exception. Le risque d’exceptions au moment de l’exécution est inhérent à l’utilisation de liaison dynamique. Si la liaison dynamique de la conversion n’est pas souhaitée, l’expression peut d’abord être convertie objecten , puis en type souhaité.

Exemple : L’exemple suivant illustre les conversions dynamiques implicites :

object o = "object";
dynamic d = "dynamic";
string s1 = o;         // Fails at compile-time – no conversion exists
string s2 = d;         // Compiles and succeeds at run-time
int i = d;             // Compiles but fails at run-time – no conversion exists

Les affectations à s2 et i les deux utilisent des conversions dynamiques implicites, où la liaison des opérations est suspendue jusqu’au moment de l’exécution. Au moment de l’exécution, les conversions implicites sont recherchées du type d’exécution (dstring) au type cible. Une conversion est trouvée en string mais pas en int.

exemple de fin

10.2.11 Conversions d’expressions constantes implicites

Une conversion d’expression constante implicite permet les conversions suivantes :

  • Un constant_expression (§12.23) de type int peut être converti en type sbyte, , byte, shortushort, uintou ulong, à condition que la valeur du constant_expression se trouve dans la plage du type de destination.
  • Une constant_expression de type long peut être convertie en type ulong, à condition que la valeur du constant_expression ne soit pas négative.

10.2.12 Conversions implicites impliquant des paramètres de type

Pour un type_parameterT connu comme un type de référence (§15.2.5), les conversions de référence implicites suivantes (§10.2.8) existent :

  • De T à sa classe Cde base effective , de T vers n’importe quelle classe de base de C, et de T vers n’importe quelle interface implémentée par C.
  • De T vers un interface_typeI dans T's effective interface set and from T to any base interface of I.
  • À partir d’un paramètre de type fourni qui TU dépend (T).U

    Remarque : Étant T donné qu’il est connu comme un type de référence, dans l’étendue de , le type d’exécution de T sera toujours un type référence, même s’il U n’est pas connu pour être un type de référence au moment de Ula compilation. Note de fin

  • Du littéral Null (§6.4.5.7) à T.

Pour un T qui n’est pas connu comme un type de référence §15.2.5, les conversions suivantes impliquant T la boxing (§10.2.9) au moment de la compilation sont considérées comme des conversions de boxe. Au moment de l’exécution, s’il s’agit T d’un type valeur, la conversion est exécutée en tant que conversion de boxe. Au moment de l’exécution, s’il s’agit T d’un type de référence, la conversion est exécutée en tant que conversion de référence implicite ou conversion d’identité.

  • De T à sa classe Cde base effective , de T vers n’importe quelle classe de base de C, et de T vers n’importe quelle interface implémentée par C.

    Remarque : C sera l’un des types System.Object, System.ValueTypeou System.Enum (sinon T serait connu comme un type référence). Note de fin

  • De T vers un interface_typeI dans T's effective interface set and from T to any base interface of I.

Pour un T qui n’est pas connu pour être un type de référence, une conversion implicite d’un paramètre T de U type fourni T dépend Ude . Au moment de l’exécution, s’il s’agit T d’un type valeur et U qu’il s’agit d’un type référence, la conversion est exécutée en tant que conversion de boxe. Au moment de l’exécution, si les deux T types sont U des types valeur, et TU sont nécessairement le même type et qu’aucune conversion n’est effectuée. Au moment de l’exécution, s’il s’agit T d’un type référence, il U s’agit nécessairement d’un type référence et la conversion est exécutée en tant que conversion de référence implicite ou conversion d’identité (§15.2.5).

Les conversions implicites suivantes existent pour un paramètre Tde type donné :

  • De T à un type S référence s’il a une conversion implicite en type S₀ référence et S₀ a une conversion d’identité en S. Au moment de l’exécution, la conversion est exécutée de la même façon que la conversion en S₀.
  • De T à un type I d’interface s’il a une conversion implicite en type I₀d’interface et I₀ est convertible en I variance vers (§18.2.3.3). Au moment de l’exécution, s’il s’agit T d’un type valeur, la conversion est exécutée en tant que conversion de boxe. Sinon, la conversion est exécutée en tant que conversion de référence implicite ou conversion d’identité.

Dans tous les cas, les règles garantissent qu’une conversion est exécutée en tant que conversion de boxe si et uniquement si, au moment de l’exécution, la conversion est d’un type valeur à un type référence.

10.2.13 Conversions de tuple implicites

Une conversion implicite existe d’une expression E tuple vers un type T tuple si E elle a la même arité que T et qu’une conversion implicite existe de chaque élément dans E le type d’élément correspondant dans T. La conversion est effectuée en créant une instance de Ttype correspondant System.ValueTuple<...> et en initialisant chacun de ses champs dans l’ordre de gauche à droite en évaluant l’expression d’élément tuple correspondante de E, en la convertissant en type d’élément correspondant de l’utilisation de T la conversion implicite trouvée et en initialisant le champ avec le résultat.

Si un nom d’élément dans l’expression tuple ne correspond pas à un nom d’élément correspondant dans le type tuple, un avertissement doit être émis.

Exemple :

(int, string) t1 = (1, "One");
(byte, string) t2 = (2, null);
(int, string) t3 = (null, null);        // Error: No conversion
(int i, string s) t4 = (i: 4, "Four");
(int i, string) t5 = (x: 5, s: "Five"); // Warning: Names are ignored

Les déclarations , t1t2t4 et t5 sont toutes valides, car les conversions implicites existent des expressions d’élément vers les types d’éléments correspondants. La déclaration de t3 n’est pas valide, car il n’y a pas de conversion en nullint. La déclaration de causes d’un avertissement, car les noms d’éléments t5 dans l’expression tuple diffèrent de ceux du type tuple.

exemple de fin

10.2.14 Conversions implicites définies par l’utilisateur

Une conversion implicite définie par l’utilisateur se compose d’une conversion implicite standard facultative, suivie de l’exécution d’un opérateur de conversion implicite défini par l’utilisateur, suivie d’une autre conversion implicite standard facultative. Les règles exactes d’évaluation des conversions implicites définies par l’utilisateur sont décrites dans le §10.5.4.

10.2.15 Conversions de fonctions anonymes et conversions de groupes de méthodes

Les fonctions anonymes et les groupes de méthodes n’ont pas de types en eux-mêmes, mais ils peuvent être convertis implicitement en types délégués. En outre, certaines expressions lambda peuvent être converties implicitement en types d’arborescence d’expressions. Les conversions de fonction anonyme sont décrites plus en détail dans le §10.7 et les conversions de groupes de méthodes dans le §10.8.

10.2.16 Conversions littérales par défaut

Une conversion implicite existe d’un default_literal (§12.8.21) en n’importe quel type. Cette conversion produit la valeur par défaut (§9.3) du type déduit.

10.2.17 Conversions de levée implicites

Bien que les expressions levées n’aient pas de type, elles peuvent être converties implicitement en n’importe quel type.

10.3 Conversions explicites

10.3.1 Général

Les conversions suivantes sont classées comme conversions explicites :

  • Toutes les conversions implicites (§10.2)
  • Conversions numériques explicites (§10.3.2)
  • Conversions d’énumération explicites (§10.3.3)
  • Conversions nullables explicites (§10.3.4)
  • Conversions de tuple explicites (§10.3.6)
  • Conversions de référence explicites (§10.3.5)
  • Conversions d’interface explicites
  • Conversions d’annulation de boîtes de réception (§10.3.7)
  • Conversions explicites de paramètres de type (§10.3.8)
  • Conversions explicites définies par l’utilisateur (§10.3.9)

Les conversions explicites peuvent se produire dans les expressions de cast (§12.9.7).

L’ensemble de conversions explicites inclut toutes les conversions implicites.

Remarque : Cela permet, par exemple, d’utiliser un cast explicite lorsqu’une conversion d’identité implicite existe, afin de forcer la sélection d’une surcharge de méthode particulière. Note de fin

Les conversions explicites qui ne sont pas des conversions implicites sont des conversions qui ne peuvent pas être prouvées pour réussir, les conversions qui sont connues pour perdre des informations et les conversions entre les domaines de types suffisamment différents pour mériter la notation explicite.

10.3.2 Conversions numériques explicites

Les conversions numériques explicites sont les conversions d’une numeric_type vers une autre numeric_type pour laquelle une conversion numérique implicite (§10.2.3) n’existe pas déjà :

  • De sbyte à byte, , ushort, uint, ulong, ou char.
  • De byte à sbyte ou char.
  • De short à sbyte, , byte, ushortuint, , ulong, ou char.
  • De ushort à sbyte, byte, , shortou char.
  • De int à sbyte, , byte, shortushort, , uint, ulong, ou char.
  • De uint à sbyte, , byte, shortushort, , int, ou char.
  • De long à sbyte, , byte, shortushort, int, , uint, ulong, ou char.
  • De ulong à sbyte, , byte, shortushort, int, , uint, long, ou char.
  • De char à sbyte, byteou short.
  • De float à sbyte, byteshortushort, , intuintlongulongchar, , ou .decimal
  • De double à sbyte, byteshortushort, int, uintlongulongcharfloatou .decimal
  • De decimal à sbyte, byteshortushort, int, uintlongulongcharfloatou .double

Étant donné que les conversions explicites incluent toutes les conversions numériques implicites et explicites, il est toujours possible de convertir de n’importe quel numeric_type en toute autre numeric_type à l’aide d’une expression de cast (§12.9.7).

Les conversions numériques explicites perdent peut-être des informations ou provoquent éventuellement la levée d’exceptions. Une conversion numérique explicite est traitée comme suit :

  • Pour une conversion d’un type intégral vers un autre type intégral, le traitement dépend du contexte de vérification de dépassement (§12.8.20) dans lequel la conversion a lieu :
    • Dans un checked contexte, la conversion réussit si la valeur de l’opérande source se trouve dans la plage du type de destination, mais lève une System.OverflowException valeur si la valeur de l’opérande source est en dehors de la plage du type de destination.
    • Dans un unchecked contexte, la conversion réussit toujours et se poursuit comme suit.
      • Si le type source est supérieur au type de destination, la valeur source est tronquée en ignorant ses bits « supplémentaires » les plus significatifs. Le résultat est ensuite traité en tant que valeur du type de destination.
      • Si le type source est de la même taille que le type de destination, la valeur source est traitée comme une valeur du type de destination
  • Pour une conversion d’un decimal type intégral, la valeur source est arrondie à zéro à la valeur intégrale la plus proche, et cette valeur intégrale devient le résultat de la conversion. Si la valeur intégrale résultante est en dehors de la plage du type de destination, une System.OverflowException valeur est levée.
  • Pour une conversion depuis float ou double vers un type intégral, le traitement dépend du contexte de contrôle de dépassement de capacité (§12.8.20) dans lequel la conversion a lieu :
    • Dans un contexte vérifié, la conversion se poursuit comme suit :
      • Si la valeur de l’opérande est NaN ou infinie, une System.OverflowException valeur est levée.
      • Sinon, l’opérande source est arrondi à zéro à la valeur intégrale la plus proche. Si cette valeur intégrale se trouve dans la plage du type de destination, cette valeur est le résultat de la conversion.
      • Sinon, une exception System.OverflowException est levée.
    • Dans un contexte non vérifié, la conversion réussit toujours et se poursuit comme suit.
      • Si la valeur de l’opérande est NaN ou infinie, le résultat de la conversion est une valeur non spécifiée du type de destination.
      • Sinon, l’opérande source est arrondi à zéro à la valeur intégrale la plus proche. Si cette valeur intégrale se trouve dans la plage du type de destination, cette valeur est le résultat de la conversion.
      • Sinon, le résultat de la conversion est une valeur non spécifiée du type de destination.
  • Pour une conversion de double vers float, la double valeur est arrondie à la valeur la plus float proche. Si la double valeur est trop petite pour représenter en tant que float, le résultat devient zéro avec le même signe que la valeur. Si l’ampleur de la double valeur est trop grande pour représenter en tant floatque , le résultat devient infini avec le même signe que la valeur. Si la double valeur est NaN, le résultat est également NaN.
  • Pour une conversion de float ou double vers decimal, la valeur source est convertie en decimal représentation et arrondie au nombre le plus proche si nécessaire (§8.3.8).
    • Si la valeur source est trop petite pour représenter en tant que decimal, le résultat devient zéro, en conservant le signe de la valeur d’origine si elle decimal prend en charge les valeurs zéro signées.
    • Si l’ampleur de la valeur source est trop grande pour représenter en tant que decimalvaleur , ou que cette valeur est infini, le résultat est l’infini préservant le signe de la valeur d’origine, si la représentation décimale prend en charge les infinis ; sinon, une exception System.OverflowException est levée.
    • Si la valeur source est NaN, le résultat est NaN si la représentation décimale prend en charge naNs ; sinon, une exception System.OverflowException est levée.
  • Pour une conversion de decimal vers float ou double, la decimal valeur est arrondie au plus double proche ou float à la valeur. Si l’ampleur de la valeur source est trop grande pour représenter dans le type cible ou que cette valeur est infini, le résultat est l’infini préservant le signe de la valeur d’origine. Si la valeur source est NaN, le résultat est NaN. Même si cette conversion peut perdre de précision, elle n’entraîne jamais la levée d’une exception.

Remarque : Le decimal type n’est pas nécessaire pour prendre en charge les valeurs infinis ou NaN, mais peut le faire ; sa plage peut être inférieure à la plage et floatdouble, mais n’est pas garantie. Pour decimal les représentations sans valeurs Infinis ou NaN, et avec une plage inférieure floatà , le résultat d’une conversion vers decimal l’un ou float l’autre double ne sera jamais infini ou NaN. Note de fin

10.3.3 Conversions d’énumération explicites

Les conversions d’énumération explicites sont les suivantes :

  • De sbyte, byteshortushortintuintlongulongcharfloatdoubleou decimal à n’importe quel enum_type.
  • De n’importe quel enum_type à sbyte, , byteshort, ushortintuintlongulongchar, float, doubleou .decimal
  • De n’importe quel enum_type à tout autre enum_type.

Une conversion d’énumération explicite entre deux types est traitée en traitant tout enum_type participant comme le type sous-jacent de cette enum_type, puis en effectuant une conversion numérique implicite ou explicite entre les types résultants.

Exemple : étant donné un enum_typeE avec et un type sous-jacent de int, une conversion de E vers byte est traitée comme une conversion numérique explicite (§10.3.2) de int vers , et une conversion de byte vers byteE est traitée comme une conversion numérique implicite (§10.2.3) de à bytepartir de int . exemple de fin

10.3.4 Conversions nullables explicites

Les conversions nullables explicites sont ces conversions nullables (§10.6.1) dérivées de conversions explicites et implicites prédéfinies.

10.3.5 Conversions de référence explicites

Les conversions de référence explicites sont les suivantes :

  • De l’objet à tout autre reference_type.
  • De n’importe quel class_type à n’importe quel T
  • De tout class_type à n’importe quel Sinterface_typeT, fourni S n’est pas scellé et fourni S n’implémente Tpas .
  • De n’importe quel interface_typeSquel class_typeT, fourni T n’est pas scellé ou fourni T implémente S.
  • De n’importe quel interface_typeSquel interface_typeT, fourni S n’est pas dérivé de T.
  • D’un S avec un type Sᵢ d’élément à un T avec un type Tᵢd’élément , à condition que toutes les valeurs suivantes soient remplies :
    • S et T diffèrent uniquement dans le type d’élément. En d’autres termes, S et T ont le même nombre de dimensions.
    • Une conversion de référence explicite existe depuis SᵢTᵢ.
  • Depuis System.Array et les interfaces qu’il implémente, à n’importe quelle array_type.
  • D’un array_typeS[]vers System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyList<T>et ses interfaces de base, à condition qu’il existe une conversion d’identité ou une conversion de référence explicite de S vers T.
  • Depuis , System.Collections.Generic.IList<S>et leurs interfaces de System.Collections.Generic.IReadOnlyList<S>base vers un type T[]de tableau unidimensionnel , à condition qu’il existe une conversion d’identité ou une conversion de référence explicite de S T.
  • Depuis System.Delegate et les interfaces qu’il implémente à n’importe quelle delegate_type.
  • D’un type S référence à un type T de référence s’il a une conversion de référence explicite depuis S vers un type T₀ de référence et T₀ qu’il existe une conversion d’identité à T₀partir de T .
  • D’un type S référence à une interface ou à un type T délégué s’il existe une conversion de référence explicite d’une S interface ou d’un type T₀ délégué et est T₀ convertible TT en variance-convertible en T₀§18.2.3.3.
  • De D<S₁...Sᵥ> là où D<T₁...Tᵥ>D<X₁...Xᵥ> est un type de délégué générique, D<S₁...Sᵥ> n’est pas compatible avec ou identique à D<T₁...Tᵥ>, et pour chaque paramètre Xᵢ de type des D éléments suivants contient :
    • Si Xᵢ elle est invariante, Sᵢ elle est identique à Tᵢ.
    • Si Xᵢ elle est covariante, il existe une conversion d’identité, une conversion de référence implicite ou une conversion de référence explicite depuis SᵢTᵢ.
    • Si Xᵢ elle est contravariante, Sᵢ et Tᵢ sont identiques ou les deux types de référence.
  • Conversions explicites impliquant des paramètres de type connus pour être des types de référence. Pour plus d’informations sur les conversions explicites impliquant des paramètres de type, consultez §10.3.8.

Les conversions de référence explicites sont ces conversions entre les reference_typequi nécessitent des vérifications au moment de l’exécution pour s’assurer qu’elles sont correctes.

Pour qu’une conversion de référence explicite réussisse au moment de l’exécution, la valeur de l’opérande source doit être null, ou le type de l’objet référencé par l’opérande source doit être un type qui peut être converti en type de destination par une conversion de référence implicite (§10.2.8). Si une conversion de référence explicite échoue, une System.InvalidCastException valeur est levée.

Remarque : les conversions de références, implicites ou explicites, ne modifient jamais la valeur de la référence elle-même (§8.2.1), seul son type ; ni la valeur de l’objet référencé. Note de fin

10.3.6 Conversions de tuple explicites

Une conversion explicite existe d’une expression E tuple vers un type T tuple si E elle a la même arité que T et qu’une conversion implicite ou explicite existe de chaque élément dans E le type d’élément correspondant dans T. La conversion est effectuée en créant une instance du Ttype correspondant System.ValueTuple<...> et en initialisant chacun de ses champs dans l’ordre de gauche à droite en évaluant l’expression d’élément tuple correspondante de E, en la convertissant en type d’élément correspondant de l’utilisation de T la conversion explicite trouvée et en initialisant le champ avec le résultat.

10.3.7 Conversions d’annulation de boîtes de réception

Une conversion d’unboxing permet à un reference_type d’être explicitement converti en value_type. Les conversions d’unboxing suivantes existent :

  • Du type object à n’importe quelle value_type.
  • Du type System.ValueType à n’importe quelle value_type.
  • Du type System.Enum à n’importe quel enum_type.
  • De n’importe quelle interface_type à n’importe quelle non_nullable_value_type qui implémente le interface_type.
  • De n’importe quel interface_type à n’importe quel I où il existe une conversion d’annulation de boîte de réception d’un interface_type vers le I₀ non_nullable_value et une conversion d’identité de vers I.I₀
  • De n’importe quel interface_typeIquel non_nullable_value_type où il existe une conversion d’annulation de boîte de réception d’un interface_typeI₀ vers l’non_nullable_value_type et soit I₀ variance_convertible vers I ou I est convertible en variance (I₀§18.2.3.3).
  • De n’importe quel reference_type à n’importe quel nullable_value_type où il existe une conversion d’annulation de boîte de réception de reference_type vers la non_nullable_value_type sous-jacente de l’nullable_value_type.
  • À partir d’un paramètre de type qui n’est pas connu pour être un type valeur vers un type tel que la conversion est autorisée par §10.3.8.

Une opération d’annulation de réception vers un non_nullable_value_type consiste à vérifier d’abord que l’instance d’objet est une valeur boxée du non_nullable_value_type donné, puis en copiant la valeur hors de l’instance.

Unboxing vers un nullable_value_type produit la valeur Null de l’nullable_value_typesi l’opérande source est null, ou le résultat encapsulé d’unboxing de l’instance d’objet au type sous-jacent du nullable_value_type sinon.

Remarque : Se référant à la classe de boxe imaginaire décrite dans le §10.2.9, une conversion de déboxing d’une zone d’objet en une value_typeS consiste à exécuter l’expression ((S_Boxing)box).value. Ainsi, les instructions

object box = new S();
S s = (S)box;

correspondent conceptuellement à

object box = new S_Boxing(new S());
S s = ((S_Boxing)box).value;

Note de fin

Pour une conversion d’unboxing vers une non_nullable_value_type donnée pour réussir au moment de l’exécution, la valeur de l’opérande source doit être une référence à une valeur boxée de cette non_nullable_value_type. Si l’opérande source est null levée System.NullReferenceException . Si l’opérande source est une référence à un objet incompatible, un System.InvalidCastException est levée.

Pour qu’une conversion unboxing vers une nullable_value_type donnée réussisse au moment de l’exécution, la valeur de l’opérande source doit être null ou une référence à une valeur boxed de la non_nullable_value_type sous-jacente de l’nullable_value_type. Si l’opérande source est une référence à un objet incompatible, un System.InvalidCastException est levée.

10.3.8 Conversions explicites impliquant des paramètres de type

Pour un type_parameterT qui est connu comme un type de référence (§15.2.5), les conversions de référence explicites suivantes (§10.3.5) existent :

  • De la classe C de base effective de T vers T et de n’importe quelle classe de base de C vers T.
  • De n’importe quel interface_type à T.
  • De T vers n’importe quel interface_typeI fourni il n’existe pas encore de conversion de référence implicite de T vers I.
  • D’un type_parameter à fournir qui U dépend T (T).U

    Remarque : Étant T donné qu’il est connu comme un type de référence, dans l’étendue de , le type d’exécution de vous sera toujours un type référence, même s’il T n’est pas connu pour être un type de référence au moment de Ula compilation. Note de fin

Pour un type_parameterT qui n’est pas connu comme un type de référence (§15.2.5), les conversions suivantes impliquant T unboxing (§10.3.7) au moment de la compilation sont considérées comme des conversions deboxing. Au moment de l’exécution, s’il s’agit T d’un type valeur, la conversion est exécutée en tant que conversion d’annulation de boîte de réception. Au moment de l’exécution, s’il s’agit T d’un type de référence, la conversion est exécutée en tant que conversion de référence explicite ou conversion d’identité.

  • De la classe C de base effective de T vers T et de n’importe quelle classe de base de C vers T.

    Remarque : C sera l’un des types System.Object, System.ValueTypeou System.Enum (sinon T serait connu comme un type référence). Note de fin

  • De n’importe quel interface_type à T.

Pour un type_parameterT qui n’est pas connu comme un type de référence (§15.2.5), les conversions explicites suivantes existent :

  • De T vers n’importe quel interface_typeI fourni il n’existe pas déjà de conversion implicite de T vers I. Cette conversion se compose d’une conversion de boxe implicite (§10.2.9) à T partir d’une object conversion de référence explicite de object vers I. Au moment de l’exécution, s’il s’agit T d’un type valeur, la conversion est exécutée en tant que conversion de boxing suivie d’une conversion de référence explicite. Au moment de l’exécution, s’il s’agit T d’un type de référence, la conversion est exécutée en tant que conversion de référence explicite.
  • D’un paramètre U de type à T fournir qui T dépend U (§15.2.5). Au moment de l’exécution, s’il s’agit T d’un type valeur et U qu’il s’agit d’un type référence, la conversion est exécutée en tant que conversion d’annulation de boîte de réception. Au moment de l’exécution, si les deux T types sont U des types valeur, et TU sont nécessairement le même type et qu’aucune conversion n’est effectuée. Au moment de l’exécution, s’il s’agit T d’un type référence, il U s’agit nécessairement d’un type référence et la conversion est exécutée en tant que conversion de référence explicite ou conversion d’identité.

Dans tous les cas, les règles garantissent qu’une conversion est exécutée en tant que conversion de déboxing si et uniquement si, au moment de l’exécution, la conversion est d’un type référence à un type valeur.

Les règles ci-dessus n’autorisent pas une conversion explicite directe d’un paramètre de type non contraint vers un type non interface, ce qui peut être surprenant. La raison de cette règle est d’éviter toute confusion et de rendre la sémantique de ces conversions claire.

Exemple : Considérez la déclaration suivante :

class X<T>
{
    public static long F(T t)
    {
        return (long)t;         // Error
    }
}

Si la conversion explicite directe d’en tlong était autorisée, on peut s’attendre facilement à ce que cela X<int>.F(7) retourne 7L. Toutefois, ce n’est pas le cas, car les conversions numériques standard ne sont prises en compte que lorsque les types sont connus comme numériques au moment de la liaison. Pour effacer la sémantique, l’exemple ci-dessus doit être écrit à la place :

class X<T>
{
    public static long F(T t)
    {
        return (long)(object)t;         // Ok, but will only work when T is long
    }
}

Ce code est maintenant compilé, mais l’exécution lève ensuite une exception au moment de l’exécution X<int>.F(7) , car un boxed int ne peut pas être converti directement en un long.

exemple de fin

10.3.9 Conversions explicites définies par l’utilisateur

Une conversion explicite définie par l’utilisateur se compose d’une conversion explicite standard facultative, suivie de l’exécution d’un opérateur de conversion implicite ou explicite défini par l’utilisateur, suivie d’une autre conversion explicite standard facultative. Les règles exactes d’évaluation des conversions explicites définies par l’utilisateur sont décrites dans le §10.5.5.

Conversions standard 10.4

10.4.1 Général

Les conversions standard sont les conversions prédéfinies qui peuvent se produire dans le cadre d’une conversion définie par l’utilisateur.

10.4.2 Conversions implicites standard

Les conversions implicites suivantes sont classées comme conversions implicites standard :

  • Conversions d’identités (§10.2.2)
  • Conversions numériques implicites (§10.2.3)
  • Conversions nullables implicites (§10.2.6)
  • Conversions littérales Null (§10.2.7)
  • Conversions de référence implicites (§10.2.8)
  • Conversions de boxe (§10.2.9)
  • Conversions d’expressions constantes implicites (§10.2.11)
  • Conversions implicites impliquant des paramètres de type (§10.2.12)

Les conversions implicites standard excluent spécifiquement les conversions implicites définies par l’utilisateur.

10.4.3 Conversions explicites standard

Les conversions explicites standard sont toutes les conversions implicites standard ainsi que le sous-ensemble des conversions explicites pour lesquelles une conversion implicite standard opposée existe.

Remarque : En d’autres termes, si une conversion implicite standard existe d’un type A en type B, une conversion explicite standard existe de type A en B type et de type B en type A. Note de fin

Conversions définies par l’utilisateur 10.5

10.5.1 Général

C# permet aux conversions implicites et explicites prédéfinies d’être augmentées par des conversions définies par l’utilisateur. Les conversions définies par l’utilisateur sont introduites en déclarant des opérateurs de conversion (§15.10.4) dans les types de classe et de struct.

10.5.2 Conversions autorisées définies par l’utilisateur

C# autorise uniquement certaines conversions définies par l’utilisateur à être déclarées. En particulier, il n’est pas possible de redéfinir une conversion implicite ou explicite déjà existante.

Pour un type source et un type STcible donnés, si S ou T sont des types valeur nullables, laissez S₀T₀ et faites référence à leurs types sous-jacents, sinonS₀, et T₀ sont égaux S et T respectivement. Une classe ou un struct est autorisé à déclarer une conversion d’un type source vers un type ST cible uniquement si toutes les valeurs suivantes sont vraies :

  • S₀ et T₀ sont des types différents.
  • S₀ Ou T₀ est le type de classe ou de struct dans lequel la déclaration d’opérateur a lieu.
  • Ni aucun S₀ n’est T₀ un interface_type.
  • À l’exclusion des conversions définies par l’utilisateur, une conversion n’existe pas depuis S ou T depuis T vers S.

Les restrictions qui s’appliquent aux conversions définies par l’utilisateur sont spécifiées dans le §15.10.4.

10.5.3 Évaluation des conversions définies par l’utilisateur

Une conversion définie par l’utilisateur convertit une expression source, qui peut avoir un type source, en un autre type, appelé type cible. L’évaluation d’une conversion définie par l’utilisateur se concentre sur la recherche de l’opérateur de conversion défini par l’utilisateur le plus spécifique pour l’expression source et le type cible. Cette détermination est divisée en plusieurs étapes :

  • Recherche de l’ensemble de classes et de structs à partir desquels les opérateurs de conversion définis par l’utilisateur seront pris en compte. Cet ensemble se compose du type source et de ses classes de base, si le type source existe, ainsi que du type cible et de ses classes de base. À cet effet, il est supposé que seules les classes et les structs peuvent déclarer des opérateurs définis par l’utilisateur, et que les types non-classes n’ont pas de classes de base. En outre, si le type source ou cible est un type nullable-value, son type sous-jacent est utilisé à la place.
  • À partir de cet ensemble de types, déterminer quels opérateurs de conversion définis par l’utilisateur et lifted sont applicables. Pour qu’un opérateur de conversion soit applicable, il est possible d’effectuer une conversion standard (§10.4) de l’expression source au type d’opérande de l’opérateur, et il est possible d’effectuer une conversion standard du type de résultat de l’opérateur vers le type cible.
  • À partir de l’ensemble d’opérateurs définis par l’utilisateur applicables, déterminant quel opérateur est sans ambiguïté le plus spécifique. En général, l’opérateur le plus spécifique est l’opérateur dont le type d’opérande est « le plus proche » de l’expression source et dont le type de résultat est « le plus proche » du type cible. Les opérateurs de conversion définis par l’utilisateur sont préférés aux opérateurs de conversion levés. Les règles exactes pour établir l’opérateur de conversion défini par l’utilisateur le plus spécifique sont définies dans les sous-sections suivantes.

Une fois qu’un opérateur de conversion défini par l’utilisateur le plus spécifique a été identifié, l’exécution réelle de la conversion définie par l’utilisateur implique jusqu’à trois étapes :

  • Tout d’abord, si nécessaire, effectuer une conversion standard de l’expression source vers le type d’opérande de l’opérateur de conversion défini ou levé par l’utilisateur.
  • Ensuite, appel de l’opérateur de conversion défini ou lifté par l’utilisateur pour effectuer la conversion.
  • Enfin, si nécessaire, effectuer une conversion standard du type de résultat de l’opérateur de conversion défini par l’utilisateur vers le type cible.

L’évaluation d’une conversion définie par l’utilisateur n’implique jamais plus d’un opérateur de conversion défini par l’utilisateur ou levé. En d’autres termes, une conversion de type en type S n’exécute jamais d’abord une conversion définie par l’utilisateur vers TS, puis exécute une conversion définie par l’utilisateur à Xpartir de X .T

  • Les définitions exactes de l’évaluation des conversions implicites ou explicites définies par l’utilisateur sont fournies dans les sous-sections suivantes. Les définitions utilisent les termes suivants :
  • Si une conversion implicite standard (§10.4.2) existe d’un type à un type AB, et si aucun n’est A interface_type Bs, il A est dit être englobant parB , et B est dit englober .A
  • Si une conversion implicite standard (§10.4.2) existe d’une expression E en un typeB, et si aucun B ni le type de E (s’il en a un) ne sont interface_types, il E est dit être B, et B est dit engloberE.
  • Le type le plus englobant dans un ensemble de types est celui qui englobe tous les autres types de l’ensemble. Si aucun type unique n’englobe tous les autres types, l’ensemble n’a pas de type le plus englobant. En termes plus intuitifs, le type le plus englobant est le type « le plus grand » dans l’ensemble, le type auquel chacun des autres types peut être converti implicitement.
  • Le type le plus englobant dans un ensemble de types est celui qui est englobant par tous les autres types de l’ensemble. Si aucun type unique n’est englobant par tous les autres types, l’ensemble n’a pas de type le plus englobant. En termes plus intuitifs, le type le plus englobant est le type le plus petit dans l’ensemble , celui qui peut être converti implicitement en chacun des autres types.

10.5.4 Conversions implicites définies par l’utilisateur

Une conversion implicite définie par l’utilisateur d’une expression E vers un type T est traitée comme suit :

  • Déterminez les types Set S₀T₀ .

    • S’il E a un type, laissez-le S être.
    • Si S ou T sont des types valeur nullables, laissez Sᵢ et Tᵢ soyez leurs types sous-jacents, sinon laissez Sᵢ et Tᵢ soyez S et T, respectivement.
    • Si Sᵢ ou Tᵢ sont des paramètres de type, laissez S₀ et T₀ soyez leurs classes de base effectives, sinon laissez S₀ et T₀ soyez Sₓ et Tᵢ, respectivement.
  • Recherchez l’ensemble de types, Dà partir duquel les opérateurs de conversion définis par l’utilisateur seront pris en compte. Cet ensemble se compose S₀ (s’il S₀ existe et est une classe ou un struct), des classes de base de S₀ (s’il S₀ existe et est une classe) et T₀ (s’il s’agit T₀ d’une classe ou d’un struct). Un type est ajouté au jeu D uniquement si une conversion d’identité vers un autre type déjà inclus dans le jeu n’existe pas.

  • Recherchez l’ensemble des opérateurs de conversion définis par l’utilisateur et levés applicables. U Cet ensemble se compose des opérateurs de conversion implicite définis par l’utilisateur et levés déclarés par les classes ou les structs dans D cette conversion d’un type englobant E à un type compris par T. Si U elle est vide, la conversion n’est pas définie et une erreur au moment de la compilation se produit.

    • S’il S existe et l’un des opérateurs en U conversion à partir de S, Sₓ est S.
    • Sinon, Sₓ est le type le plus englobant dans l’ensemble combiné de types sources des opérateurs dans U. Si un type le plus englobant est introuvable exactement, la conversion est ambiguë et une erreur au moment de la compilation se produit.
  • Recherchez le type cible le plus spécifique, Tₓdes opérateurs dans U:

    • Si l’un des opérateurs en U conversion Test , Tₓ est T.
    • Sinon, Tₓ est le type le plus englobant dans l’ensemble combiné de types cibles des opérateurs dans U. Si un type le plus englobant est introuvable exactement, la conversion est ambiguë et une erreur au moment de la compilation se produit.
  • Recherchez l’opérateur de conversion le plus spécifique :

    • Si U contient exactement un opérateur de conversion défini par l’utilisateur qui se convertit Sₓ en Tₓ, il s’agit de l’opérateur de conversion le plus spécifique.
    • Sinon, s’il U contient exactement un opérateur de Sₓ conversion levé qui se convertit en Tₓ, il s’agit de l’opérateur de conversion le plus spécifique.
    • Sinon, la conversion est ambiguë et une erreur au moment de la compilation se produit.
  • Enfin, appliquez la conversion :

    • Si E n’a pas encore le type Sₓ, une conversion implicite standard à partir de E celle-ci Sₓ est effectuée.
    • L’opérateur de conversion le plus spécifique est appelé pour effectuer la conversion en SₓTₓ.
    • Si Tₓ ce n’est pas Tle cas, une conversion implicite standard est TₓT effectuée.

Une conversion implicite définie par l’utilisateur d’un type S en type T existe si une conversion implicite définie par l’utilisateur existe d’une variable de type S en T.

10.5.5 Conversions explicites définies par l’utilisateur

Une conversion explicite définie par l’utilisateur d’une expression E vers un type T est traitée comme suit :

  • Déterminez les types Set S₀T₀ .
    • S’il E a un type, laissez-le S être.
    • Si S ou T sont des types valeur nullables, laissez Sᵢ et Tᵢ soyez leurs types sous-jacents, sinon laissez Sᵢ et Tᵢ soyez S et T, respectivement.
    • Si Sᵢ ou Tᵢ sont des paramètres de type, laissez S₀ et T₀ soyez leurs classes de base effectives, sinon laissez S₀ et T₀ soyez Sᵢ et Tᵢ, respectivement.
  • Recherchez l’ensemble de types, Dà partir duquel les opérateurs de conversion définis par l’utilisateur seront pris en compte. Cet ensemble se compose S₀ (s’il S₀ existe et est une classe ou un struct), des classes de S₀ base (s’il S₀ existe et est une classe), T₀ (s’il s’agit T₀ d’une classe ou d’un struct) et des classes de base de T₀ (s’il s’agit T₀ d’une classe). A le type est ajouté au jeu D uniquement si une conversion d’identité vers un autre type déjà inclus dans le jeu n’existe pas.
  • Recherchez l’ensemble des opérateurs de conversion définis par l’utilisateur et levés applicables. U Cet ensemble se compose des opérateurs de conversion implicites ou explicites définis par l’utilisateur déclarés par les classes ou les structs dans structs qui D se convertissent d’un type englobant E ou englobant par S (s’il existe) en un type englobant ou englobant par T. Si U elle est vide, la conversion n’est pas définie et une erreur au moment de la compilation se produit.
  • Recherchez le type de source le plus spécifique, Sₓdes opérateurs dans U:
    • S’il existe et l’un des opérateurs en U conversion à partir de S, Sₓ est S.
    • Sinon, si l’un des opérateurs en U conversion à partir de types qui englobent E, Sₓ est le type le plus englobant dans l’ensemble combiné de types sources de ces opérateurs. Si aucun type le plus englobant n’est trouvé, la conversion est ambiguë et une erreur au moment de la compilation se produit.
    • Sinon, Sₓ est le type le plus englobant dans l’ensemble combiné de types sources des opérateurs dans U. Si un type le plus englobant est introuvable exactement, la conversion est ambiguë et une erreur au moment de la compilation se produit.
  • Recherchez le type cible le plus spécifique, Tₓdes opérateurs dans U:
    • Si l’un des opérateurs en U conversion Test , Tₓ est T.
    • Dans le cas contraire, si l’un des opérateurs convertis U en types englobants TTₓ est le type le plus englobant dans l’ensemble combiné de types cibles de ces opérateurs. Si un type le plus englobant est introuvable exactement, la conversion est ambiguë et une erreur au moment de la compilation se produit.
    • Sinon, Tₓ est le type le plus englobant dans l’ensemble combiné de types cibles des opérateurs dans U. Si aucun type le plus englobant n’est trouvé, la conversion est ambiguë et une erreur au moment de la compilation se produit.
  • Recherchez l’opérateur de conversion le plus spécifique :
    • Si U contient exactement un opérateur de conversion défini par l’utilisateur qui se Sₓ convertit en Tₓ, il s’agit de l’opérateur de conversion le plus spécifique.
    • Sinon, s’il U contient exactement un opérateur de Sₓ conversion levé qui se convertit en Tₓ, il s’agit de l’opérateur de conversion le plus spécifique.
    • Sinon, la conversion est ambiguë et une erreur au moment de la compilation se produit.
  • Enfin, appliquez la conversion :
    • Si E ce n’est pas déjà le type Sₓ, une conversion explicite standard d’E vers Sₓ est effectuée.
    • L’opérateur de conversion défini par l’utilisateur le plus spécifique est appelé pour effectuer une conversion en SₓTₓ.
    • Si Tₓ ce n’est pas Tle cas, une conversion explicite standard est TₓT effectuée.

Une conversion explicite définie par l’utilisateur d’un type S en type T existe si une conversion explicite définie par l’utilisateur existe d’une variable de type S en T.

10.6 Conversions impliquant des types nullables

10.6.1 Conversions nullables

Les conversions nullables permettent aux conversions prédéfinies qui opèrent sur des types valeur non nullables d’être également utilisées avec des formes nullables de ces types. Pour chacune des conversions implicites ou explicites prédéfinies qui se convertissent d’un type S valeur non nullable en type T valeur non nullable (§10.2.2, §10.2.3, §10.2.4, §10.2.11, §10.3.2 et §10.3.3), les conversions nullables suivantes existent :

  • Conversion implicite ou explicite de S? vers T?
  • Conversion implicite ou explicite de S vers T?
  • Conversion explicite de S? vers T.

Une conversion nullable est elle-même classée comme une conversion implicite ou explicite.

Certaines conversions nullables sont classées comme conversions standard et peuvent se produire dans le cadre d’une conversion définie par l’utilisateur. Plus précisément, toutes les conversions nullables implicites implicites sont classées comme conversions implicites standard (§10.4.2) et ces conversions explicites nullables qui répondent aux exigences du §10.4.3 sont classées comme des conversions explicites standard.

Évaluation d’une conversion nullable en fonction d’une conversion sous-jacente à partir du ST produit suivant :

  • Si la conversion nullable est de S? :T?
    • Si la valeur source est Null (HasValue propriété), falsele résultat est la valeur Null du type T?.
    • Sinon, la conversion est évaluée comme un déballage à partir de S? , suivi de la conversion sous-jacente vers SS, suivi d’un retour à l’habillage de T vers T.T?
  • Si la conversion nullable est de à partir ST?de , la conversion est évaluée en tant que conversion sous-jacente de S vers T suivie d’une habillage de T vers T?.
  • Si la conversion nullable est de à partir S? de , la conversion est évaluée comme un déballage à partir duquel TS? la conversion sous-jacente passe S à S.T

Conversions levées 10.6.2

Étant donné un opérateur de conversion défini par l’utilisateur qui convertit d’un type S valeur non nullable en type Tvaleur non nullable, un opérateur de conversion lifted existe qui se convertit en S?T? . Cet opérateur de conversion lifted effectue un déballage à partir duquel S? la conversion définie par l’utilisateur doit SS être suivie d’un retour TTà la ligne vers , sauf qu’une valeur T? Null convertie directement en valeur S?Null.T? Un opérateur de conversion levée a la même classification implicite ou explicite que son opérateur de conversion défini par l’utilisateur sous-jacent.

10.7 Conversions de fonctions anonymes

10.7.1 Général

Une anonymous_method_expression ou lambda_expression est classée comme fonction anonyme (§12.19). L’expression n’a pas de type, mais peut être convertie implicitement en type délégué compatible. Certaines expressions lambda peuvent également être converties implicitement en un type d’arborescence d’expressions compatible.

Plus précisément, une fonction F anonyme est compatible avec un type D délégué fourni :

  • S’il F contient un anonymous_function_signature, D et F disposez du même nombre de paramètres.
  • S’il F ne contient pas de anonymous_function_signature, D il peut avoir zéro ou plusieurs paramètres d’un type, tant qu’aucun paramètre n’est un paramètre de D sortie.
  • S’il F existe une liste de paramètres typée explicitement, chaque paramètre présente D les mêmes modificateurs que le paramètre correspondant et F une conversion d’identité existe entre le paramètre correspondant dans F.
  • Si F une liste D de paramètres implicitement typée n’a pas de référence ou de paramètres de sortie.
  • Si le corps d’une F expression est une expression et D a un type de retour void ouF est asynchrone et D a un «TaskType» type de retour (§15.15.1), alors quand chaque paramètre de F est donné le type du paramètre correspondant dans D, le corps F d’une expression valide (w.r.t §12) qui serait autorisée en tant que statement_expression (§13.7).
  • Si le corps d’un F bloc est un bloc et Da un type F est asynchrone et D a un «TaskType» type de retour, alors lorsque chaque paramètre est F donné au type du paramètre correspondant dans D, le corps d’un F bloc valide (w.r.t §13.3) dans lequel aucune instruction ne return spécifie d’expression.
  • Si le corps d’une F expression est une expression, et F n’est pas asynchrone et D qu’il a un type voidde non-retourT, ouF qu’il D a un «TaskType»<T> type de retour (§15.15.1), lorsque chaque paramètre du F paramètre correspondant est donné au type du paramètre correspondant, le corps d’une D expression valide (w.r.t F) qui est implicitement convertible en T.
  • Si le corps d’un F bloc est un bloc etF qu’il n’est pas asynchrone et D a un type Tde retour non void, ouF est asynchrone et D a un «TaskType»<T> type de retour, alors lorsque chaque paramètre d’est F donné le type du paramètre correspondant dans D, le corps d’un F bloc d’instruction valide (w.r.t §13.3) avec un point de terminaison non accessible dans lequel chaque instruction de retour spécifie une expression qui est implicitement convertible en .T

Exemple : Les exemples suivants illustrent ces règles :

delegate void D(int x);
D d1 = delegate { };                         // Ok
D d2 = delegate() { };                       // Error, signature mismatch
D d3 = delegate(long x) { };                 // Error, signature mismatch
D d4 = delegate(int x) { };                  // Ok
D d5 = delegate(int x) { return; };          // Ok
D d6 = delegate(int x) { return x; };        // Error, return type mismatch

delegate void E(out int x);
E e1 = delegate { };                         // Error, E has an output parameter
E e2 = delegate(out int x) { x = 1; };       // Ok
E e3 = delegate(ref int x) { x = 1; };       // Error, signature mismatch

delegate int P(params int[] a);
P p1 = delegate { };                         // Error, end of block reachable
P p2 = delegate { return; };                 // Error, return type mismatch
P p3 = delegate { return 1; };               // Ok
P p4 = delegate { return "Hello"; };         // Error, return type mismatch
P p5 = delegate(int[] a)                     // Ok
{
    return a[0];
};
P p6 = delegate(params int[] a)              // Error, params modifier
{
    return a[0];
};
P p7 = delegate(int[] a)                     // Error, return type mismatch
{
    if (a.Length > 0) return a[0];
    return "Hello";
};

delegate object Q(params int[] a);
Q q1 = delegate(int[] a)                    // Ok
{
    if (a.Length > 0) return a[0];
    return "Hello";
};

exemple de fin

Exemple : Les exemples qui suivent utilisent un type Func<A,R> délégué générique qui représente une fonction qui prend un argument de type A et retourne une valeur de type R:

delegate R Func<A,R>(A arg);

Dans les affectations

Func<int,int> f1 = x => x + 1; // Ok
Func<int,double> f2 = x => x + 1; // Ok
Func<double,int> f3 = x => x + 1; // Error
Func<int, Task<int>> f4 = async x => x + 1; // Ok

le paramètre et les types de retour de chaque fonction anonyme sont déterminés à partir du type de la variable à laquelle la fonction anonyme est affectée.

La première affectation convertit correctement la fonction anonyme en type Func<int,int> délégué, car, lorsqu’elle x est donnée int, x + 1 est une expression valide qui est implicitement convertible en type int.

De même, la deuxième affectation convertit correctement la fonction anonyme en type délégué Func<int, double> car le résultat (de x + 1 type int) est implicitement convertible en type double.

Toutefois, la troisième affectation est une erreur au moment de la compilation, car, lorsqu’elle x est donnée double, le résultat ( x + 1 de type double) n’est pas implicitement convertible en type int.

La quatrième affectation convertit correctement la fonction asynchrone anonyme en type Func<int, Task<int>> délégué, car le résultat ( x + 1 de type int) est implicitement convertible en type int de retour effectif du lambda asynchrone, qui a un type Task<int>de retour .

exemple de fin

Une expression lambda est compatible avec un type F d’arborescence d’expressions Expression<D> si F elle est compatible avec le type Ddélégué. Cela ne s’applique pas aux méthodes anonymes, seules les expressions lambda.

Les fonctions anonymes peuvent influencer la résolution de surcharge et participer à l’inférence de type. Pour plus d’informations, consultez le §12.6 .

10.7.2 Évaluation des conversions de fonctions anonymes en types délégués

La conversion d’une fonction anonyme en type délégué produit une instance de délégué qui référence la fonction anonyme et l’ensemble (éventuellement vide) de variables externes capturées actives au moment de l’évaluation. Lorsque le délégué est appelé, le corps de la fonction anonyme est exécuté. Le code du corps est exécuté à l’aide de l’ensemble de variables externes capturées référencées par le délégué. Une delegate_creation_expression (§12.8.17.6) peut être utilisée comme syntaxe alternative pour convertir une méthode anonyme en type délégué.

La liste d’appel d’un délégué produit à partir d’une fonction anonyme contient une entrée unique. L’objet cible exact et la méthode cible du délégué ne sont pas spécifiés. En particulier, il n’est pas spécifié si l’objet cible du délégué est null, la this valeur du membre de la fonction englobante ou d’un autre objet.

Les conversions de fonctions anonymes identiques sémantiquement avec le même ensemble (éventuellement vide) d’instances de variables externes capturées vers les mêmes types délégués sont autorisées (mais pas obligatoires) à retourner la même instance de délégué. Le terme sémantiquement identique est utilisé ici pour signifier que l’exécution des fonctions anonymes produira, dans tous les cas, les mêmes effets étant donné les mêmes arguments. Cette règle autorise l’optimisation du code tel que le suivant.

delegate double Function(double x);

class Test
{
    static double[] Apply(double[] a, Function f)
    {
        double[] result = new double[a.Length];
        for (int i = 0; i < a.Length; i++)
        {
            result[i] = f(a[i]);
        }
        return result;
    }

    static void F(double[] a, double[] b)
    {
        a = Apply(a, (double x) => Math.Sin(x));
        b = Apply(b, (double y) => Math.Sin(y));
        ...
    }
}

Étant donné que les deux délégués de fonction anonyme ont le même ensemble (vide) de variables externes capturées et que les fonctions anonymes sont sémantiquement identiques, un compilateur est autorisé à faire référence aux mêmes méthodes cibles. En effet, un compilateur est autorisé à retourner la même instance de délégué à partir de deux expressions de fonction anonymes.

10.7.3 Évaluation des conversions d’expressions lambda en types d’arborescence d’expressions

La conversion d’une expression lambda en type d’arborescence d’expressions produit une arborescence d’expressions (§8.6). Plus précisément, l’évaluation de la conversion d’expression lambda produit une structure d’objet qui représente la structure de l’expression lambda elle-même.

Toutes les expressions lambda ne peuvent pas être converties en types d’arborescence d’expressions. La conversion en type délégué compatible existe toujours, mais elle peut échouer au moment de la compilation pour des raisons définies par l’implémentation.

Remarque : Les raisons courantes pour lesquelles une expression lambda ne peut pas être convertie en type d’arborescence d’expressions sont les suivantes :

  • Il a un corps de bloc
  • Il a le async modificateur
  • Il contient un opérateur d’affectation
  • Il contient un paramètre de sortie ou de référence
  • Il contient une expression liée dynamiquement

Note de fin

Conversions de groupes de méthodes 10.8

Une conversion implicite existe d’un groupe de méthodes (§12.2) en type délégué compatible (§20.4). S’il D s’agit d’un type délégué et E est une expression classifiée en tant que groupe de méthodes, elle D est compatible avec E si et seulement si E elle contient au moins une méthode applicable dans sa forme normale (§12.6.4.2) à n’importe quelle liste d’arguments (§12.6.2) ayant des types et des modificateurs correspondant aux types de paramètres et modificateurs de D, comme décrit dans la section suivante.

L’application au moment de la compilation de la conversion d’un groupe E de méthodes en type D délégué est décrite dans la section suivante.

  • Une seule méthode M est sélectionnée correspondant à un appel de méthode (§12.8.10.2) du formulaire E(A), avec les modifications suivantes :
    • La liste A d’arguments est une liste d’expressions, chacune classifiée en tant que variable et avec le type et le modificateur (in, outou ref) du paramètre correspondant dans la parameter_list de — à l’exception des D paramètres de type dynamic, où l’expression correspondante a le type object au lieu de dynamic.
    • Les méthodes candidates considérées sont uniquement celles applicables sous leur forme normale et n’omettent aucun paramètre facultatif (§12.6.4.2). Par conséquent, les méthodes candidates sont ignorées si elles sont applicables uniquement dans leur forme développée, ou si un ou plusieurs de leurs paramètres facultatifs n’ont pas de paramètre correspondant dans D.
  • Une conversion est considérée comme étant existante si l’algorithme de §12.8.10.2D
  • Si la méthode sélectionnée est une méthode M d’instance, l’expression d’instance associée détermine E l’objet cible du délégué.
  • Si la méthode sélectionnée est une méthode M d’extension qui est indiquée par le biais d’un accès membre sur une expression d’instance, cette expression d’instance détermine l’objet cible du délégué.
  • Le résultat de la conversion est une valeur de type D, à savoir un délégué qui fait référence à la méthode sélectionnée et à l’objet cible.

Exemple : L’exemple suivant illustre les conversions de groupes de méthodes :

delegate string D1(object o);
delegate object D2(string s);
delegate object D3();
delegate string D4(object o, params object[] a);
delegate string D5(int i);
class Test
{
    static string F(object o) {...}

    static void G()
    {
        D1 d1 = F;         // Ok
        D2 d2 = F;         // Ok
        D3 d3 = F;         // Error – not applicable
        D4 d4 = F;         // Error – not applicable in normal form
        D5 d5 = F;         // Error – applicable but not compatible
    }
}

Affectation pour d1 convertir implicitement le groupe F de méthodes en valeur de type D1.

L’affectation pour d2 indiquer comment il est possible de créer un délégué à une méthode qui a des types de paramètres moins dérivés (contravariants) et un type de retour plus dérivé (covariant).

L’affectation pour d3 afficher comment aucune conversion n’existe si la méthode n’est pas applicable.

Affectation pour d4 montre comment la méthode doit être applicable sous sa forme normale.

Affectation pour d5 afficher comment les types de paramètres et de retour du délégué et de la méthode sont autorisés à différer uniquement pour les types de référence.

exemple de fin

Comme avec toutes les autres conversions implicites et explicites, l’opérateur de cast peut être utilisé pour effectuer explicitement une conversion particulière.

Exemple : Ainsi, l’exemple

object obj = new EventHandler(myDialog.OkClick);

pourrait être écrit à la place

object obj = (EventHandler)myDialog.OkClick;

exemple de fin

Une conversion de groupe de méthodes peut faire référence à une méthode générique, soit en spécifiant explicitement des arguments de type dans E, soit via l’inférence de type (§12.6.3). Si l’inférence de type est utilisée, les types de paramètres du délégué sont utilisés comme types d’arguments dans le processus d’inférence. Le type de retour du délégué n’est pas utilisé pour l’inférence. Que les arguments de type soient spécifiés ou déduits, ils font partie du processus de conversion de groupe de méthodes ; il s’agit des arguments de type utilisés pour appeler la méthode cible lorsque le délégué résultant est appelé.

Exemple :

delegate int D(string s, int i);
delegate int E();

class X
{
    public static T F<T>(string s, T t) {...}
    public static T G<T>() {...}

    static void Main()
    {
        D d1 = F<int>;        // Ok, type argument given explicitly
        D d2 = F;             // Ok, int inferred as type argument
        E e1 = G<int>;        // Ok, type argument given explicitly
        E e2 = G;             // Error, cannot infer from return type
    }
}

exemple de fin

Les groupes de méthodes peuvent influencer la résolution de surcharge et participer à l’inférence de type. Pour plus d’informations, consultez le §12.6 .

L’évaluation au moment de l’exécution d’une conversion de groupe de méthodes se poursuit comme suit :

  • Si la méthode sélectionnée au moment de la compilation est une méthode d’instance ou s’il s’agit d’une méthode d’extension accessible en tant que méthode d’instance, l’objet cible du délégué est déterminé à partir de l’expression d’instance associée à E:
    • L’expression d’instance est évaluée. Si cette évaluation provoque une exception, aucune autre étape n’est exécutée.
    • Si l’expression d’instance est d’un reference_type, la valeur calculée par l’expression d’instance devient l’objet cible. Si la méthode sélectionnée est une méthode d’instance et que l’objet cible est null, un System.NullReferenceException est levée et aucune autre étape n’est exécutée.
    • Si l’expression d’instance est d’un value_type, une opération de boxe (§10.2.9) est effectuée pour convertir la valeur en objet, et cet objet devient l’objet cible.
  • Sinon, la méthode sélectionnée fait partie d’un appel de méthode statique et l’objet cible du délégué est null.
  • Une instance de délégué de type D délégué est obtenue avec une référence à la méthode qui a été déterminée au moment de la compilation et une référence à l’objet cible calculé ci-dessus, comme suit :
    • La conversion est autorisée (mais non obligatoire) pour utiliser une instance déléguée existante qui contient déjà ces références.
    • Si une instance existante n’a pas été réutilisée, une nouvelle instance est créée (§20.5). S’il n’y a pas suffisamment de mémoire disponible pour allouer la nouvelle instance, une System.OutOfMemoryException valeur est levée. Sinon, l’instance est initialisée avec les références données.