Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
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
inten typelongest implicite, de sorte que les expressions de typeintpeuvent implicitement être traitées comme du typelong. La conversion opposée, du type au typelongint, 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 intexemple 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 de membres de fonction (§12.6.6.6), les expressions de cast (§12.9.8) et les affectations (§12.23).
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
TetT, pour n’importe quel typeT. - Entre
TetT?pour n’importe quel typeTde référence . - Entre
objectetdynamic. - 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
t1ett2t3tous ont deux éléments : unintsuivi d’unstring. Les types d’éléments Tuple peuvent eux-mêmes par des tuples, comme danst4,t5ett6. 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 tuplest4,t5ett6.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.8).
10.2.3 Conversions numériques implicites
Les conversions numériques implicites sont les suivantes :
- De
sbyteàshort, ,int,longfloat, ,double, oudecimal. - De
byteàshort,ushortintuintlong,ulong,float, ,double, ou .decimal - De
shortàint, ,long,float,double, oudecimal. - De
ushortàint, ,uint,longulong, ,float,double, oudecimal. - De
intàlong,float, ,doubleoudecimal. - De
uintàlong, ,ulong,float,double, oudecimal. - De
longàfloat,doubleoudecimal. - De
ulongàfloat,doubleoudecimal. - De
charàushort, ,int,uintlong,ulong, ,float,double, oudecimal. - 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.25) 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.3
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 à
objectetdynamic. - De n’importe quel class_type à n’importe quel
S, fourniTest dérivé deS.T - De n’importe quel class_type à n’importe
S - De n’importe quel interface_type à n’importe quel interface_type
S, fourniTest dérivé deS.T - D’un avec un type
Sd’élément à unSᵢavec un type d’élément , à condition que toutes les valeurs suivantes soient remplies :-
SetTdiffèrent uniquement dans le type d’élément. En d’autres termes,SetTont le même nombre de dimensions. - Une conversion de référence implicite existe de
SᵢversTᵢ.
-
- D’un type
S[]de tableau unidimensionnel versSystem.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 deSversT. - Depuis n’importe quel array_type vers
System.Arrayet les interfaces qu’il implémente. - Depuis n’importe quel delegate_type vers
System.Delegateet 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 s’il a une identité implicite ou une conversion de référence vers un
TetT₀a une conversion d’identité enT₀. - D’un reference_type à un type
Td’interface ou de délégué s’il a une identité implicite ou une conversion de référence vers une interface ou un typeT₀délégué etT₀est convertible en variance-convertible (§19.2.3.3) versT. - 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
- De n’importe quel non_nullable_value_type à tout interface_type
Ide sorte qu’il existe une conversion de boxe de l’non_nullable_value_type vers un autre interface_typeI₀, etI₀est convertible en variance (§19.2.3.3) versI. - 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 SinterfaceI, avec une classe boxing appeléeS_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
vde typeSconsiste désormais à exécuter l’expressionnew S_Boxing(v)et à renvoyer l’instance résultante en tant que valeur du type cible de la conversion. Ainsi, les instructionsS 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
Sa le typeSd’exécution et une vérification de type runtime à l’aide de l’opérateurisavec 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 intUne 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 typeobjectmoins dérivé. Par exemple, les éléments suivantsstruct 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
pboxcauses de la copie de la valeur.pAu lieuPointde cela, la valeur 20 était déclaréeclass, carpelleboxré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 existsLes affectations à
s2etiles 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 enstringmais pas enint.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.25) de type
intpeut être converti en typesbyte, ,byte,shortushortuint, ouulong, à condition que la valeur du constant_expression se trouve dans la plage du type de destination. - Une constant_expression de type
longpeut être convertie en typeulong, à 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 classeCde base effective , deTvers n’importe quelle classe de base deC, et deTvers n’importe quelle interface implémentée parC. - De
Tvers un interface_typeIdansT's effective interface set and fromTto any base interface ofI. - À partir d’un paramètre de type fourni qui
TUdépend (T).URemarque : Étant
Tdonné qu’il est connu comme un type de référence, dans l’étendue de , le type d’exécution deTsera toujours un type référence, même s’ilUn’est pas connu pour être un type de référence au moment deUla compilation. Note de fin - Du littéral Null (§6.4.5.7) à T.
Pour un qui n’est T connu comme un type de référence §15.2.5, les conversions suivantes impliquant la boxing (T) 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 classeCde base effective , deTvers n’importe quelle classe de base deC, et deTvers n’importe quelle interface implémentée parC.Remarque :
Csera l’un des typesSystem.Object,System.ValueTypeouSystem.Enum(sinonTserait connu comme un type référence). Note de fin - De
Tvers un interface_typeIdansT's effective interface set and fromTto any base interface ofI.
Pour un qui n’est T connu pour être un type de référence, une conversion implicite d’un paramètre de T type fourni U dépend Tde . 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 typeSréférence s’il a une conversion implicite en typeS₀référence etS₀a une conversion d’identité enS. Au moment de l’exécution, la conversion est exécutée de la même façon que la conversion enS₀. - De
Tà un typeId’interface s’il a une conversion implicite en typeI₀d’interface, etI₀est convertible en variance-convertible enI(§19.2.3.3). Au moment de l’exécution, s’il s’agitTd’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 ignoredLes déclarations ,
t1t2t4ett5sont toutes valides, car les conversions implicites existent des expressions d’élément vers les types d’éléments correspondants. La déclaration det3n’est pas valide, car il n’y a pas de conversion ennullint. La déclaration de causes d’un avertissement, car les noms d’élémentst5dans 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.2.18 Conversion d’expression switch
Il existe une conversion implicite d’un switch_expression (§12.11) vers chaque type T pour lequel il existe une conversion implicite de chaqueswitch_expression_arm’switch_expression_arm_expression à T.
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.8).
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, ouchar. - De
byteàsbyteouchar. - De
shortàsbyte, ,byte,ushortuint, ,ulong, ouchar. - De
ushortàsbyte,byte, ,shortouchar. - De
intàsbyte, ,byte,shortushort, ,uint,ulong, ouchar. - De
uintàsbyte, ,byte,shortushort, ,int, ouchar. - De
longàsbyte, ,byte,shortushort,int, ,uint,ulong, ouchar. - De
ulongàsbyte, ,byte,shortushort,int, ,uint,long, ouchar. - De
charàsbyte,byteoushort. - 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 des numeric_type en toute autre numeric_type à l’aide d’une expression de cast (§12.9.8).
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
checkedcontexte, la conversion réussit si la valeur de l’opérande source se trouve dans la plage du type de destination, mais lève uneSystem.OverflowExceptionvaleur si la valeur de l’opérande source est en dehors de la plage du type de destination. - Dans un
uncheckedcontexte, 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
- Dans un
- Pour une conversion d’un
decimaltype 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, uneSystem.OverflowExceptionvaleur est levée. - Pour une conversion depuis
floatoudoublevers 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.OverflowExceptionvaleur 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.OverflowExceptionest levée.
- Si la valeur de l’opérande est NaN ou infinie, une
- 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.
- Dans un contexte vérifié, la conversion se poursuit comme suit :
- Pour une conversion de
doubleversfloat, ladoublevaleur est arrondie à la valeur la plusfloatproche. Si ladoublevaleur est trop petite pour représenter en tant quefloat, le résultat devient zéro avec le même signe que la valeur. Si l’ampleur de ladoublevaleur est trop grande pour représenter en tantfloatque , le résultat devient infini avec le même signe que la valeur. Si ladoublevaleur est NaN, le résultat est également NaN. - Pour une conversion de
floatoudoubleversdecimal, la valeur source est convertie endecimalrepré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 elledecimalprend 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.
- Si la valeur source est trop petite pour représenter en tant que
- Pour une conversion de
decimalversfloatoudouble, ladecimalvaleur est arrondie au plusdoubleproche oufloatà 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
decimaltype 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 etfloatdouble, mais n’est pas garantie. Pourdecimalles représentations sans valeurs Infinis ou NaN, et avec une plage inférieurefloatà , le résultat d’une conversion versdecimall’un oufloatl’autredoublene 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,byteshortushortintuintlongulongcharfloatdoubleoudecimalà 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_type
Eavec un type deintsous-jacent, une conversion deEenbyteest traitée comme une conversion numérique explicite (§10.3.2) deintàbyte, et une conversion debyteenEest traitée comme une conversion numérique implicite (§10.2.3) debyteà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
S - De tout class_type à n’importe quel
Sinterface_typeT, fourniSn’est pas scellé et fourniSn’implémenteTpas . - De n’importe quel interface_typequel
S, fourniTn’est pas scellé ou fourniTimplémenteT. - De n’importe quel interface_typequel
S, fourniTn’est pas dérivé deS. - D’un avec un type
Sd’élément à unSᵢavec un type d’élément , à condition que toutes les valeurs suivantes soient remplies :-
SetTdiffèrent uniquement dans le type d’élément. En d’autres termes,SetTont le même nombre de dimensions. - Une conversion de référence explicite existe depuis
SᵢTᵢ.
-
- Depuis
System.Arrayet les interfaces qu’il implémente, à n’importe quelle array_type. - D’un array_typevers
S[],System.Collections.Generic.IList<T>et ses interfaces de base, à condition qu’il existe une conversion d’identité ou une conversion de référence explicite deSystem.Collections.Generic.IReadOnlyList<T>versS. - Depuis ,
System.Collections.Generic.IList<S>et leurs interfaces deSystem.Collections.Generic.IReadOnlyList<S>base vers un typeT[]de tableau unidimensionnel , à condition qu’il existe une conversion d’identité ou une conversion de référence explicite deST. - Depuis
System.Delegateet les interfaces qu’il implémente à n’importe quelle delegate_type. - D’un type
Sréférence à un typeTde référence s’il a une conversion de référence explicite depuisSvers un typeT₀de référence etT₀qu’il existe une conversion d’identité àT₀partir deT. - D’un type
Sréférence à une interface ou à un typeTdélégué s’il existe une conversion de référence explicite d’uneSinterface ou d’un typeT₀délégué et estT₀convertibleTTen variance-convertible enT₀§19.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ètreXᵢde type desDé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 depuisSᵢTᵢ. - Si
Xᵢelle est contravariante,SᵢetTᵢsont identiques ou les deux types de référence.
- Si
- 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
Ioù il existe une conversion d’annulation de boîte de réception d’un interface_type vers leI₀non_nullable_value et une conversion d’identité de versI.I₀ - De n’importe quel interface_type
Ià n’importe quel non_nullable_value_type où il existe une conversion d’annulation de boîte de réception d’un interface_typeI₀vers le non_nullable_value_type et estI₀variance_convertible versIouIest convertible en variance versI₀(§19.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_type
Sconsiste à exécuter l’expression((S_Boxing)box).value. Ainsi, les instructionsobject 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
Cde base effective deTversTet de n’importe quelle classe de base deCversT. - De n’importe quel interface_type à
T. - De
Tvers n’importe quel interface_typeIfourni il n’existe pas encore de conversion de référence implicite deTversI. - D’un type_parameter à fournir qui
UdépendT(T).URemarque : Étant
Tdonné 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’ilTn’est pas connu pour être un type de référence au moment deUla 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
Cde base effective deTversTet de n’importe quelle classe de base deCversT.Remarque : C sera l’un des types
System.Object,System.ValueTypeouSystem.Enum(sinonTserait 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
Tvers n’importe quel interface_typeIfourni il n’existe pas déjà de conversion implicite deTversI. Cette conversion se compose d’une conversion de boxe implicite (§10.2.9) àTpartir d’uneobjectconversion de référence explicite deobjectversI. Au moment de l’exécution, s’il s’agitTd’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’agitTd’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
Ude type àTfournir quiTdépendU(§15.2.5). Au moment de l’exécution, s’il s’agitTd’un type valeur etUqu’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 deuxTtypes sontUdes types valeur, etTUsont nécessairement le même type et qu’aucune conversion n’est effectuée. Au moment de l’exécution, s’il s’agitTd’un type référence, ilUs’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 celaX<int>.F(7)retourne7L. 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 boxedintne peut pas être converti directement en unlong.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
Aen typeB, une conversion explicite standard existe de typeAenBtype et de typeBen typeA. 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₀etT₀sont des types différents. -
S₀OuT₀est le type de classe ou de struct dans lequel la déclaration d’opérateur a lieu. - Ni aucun
S₀n’estT₀un interface_type. - À l’exclusion des conversions définies par l’utilisateur, une conversion n’existe pas depuis
SouTdepuisTversS.
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
Avers un typeB, et si niAniBne sont des interface_type, alorsAest dit être englobé par , etBest ditBengloberA. - Si une conversion implicite standard (§10.4.2) existe d'une expression
Evers un typeB, et si niBni le type deE(s'il en a un) ne sont des interface_type, alorsEest dit englobé parB, etBest dit englobantE. - 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
SetS₀T₀.- S’il
Ea un type, laissez-leSêtre. - Si
SouTsont des types valeur nullables, laissezSᵢetTᵢsoyez leurs types sous-jacents, sinon laissezSᵢetTᵢsoyezSetT, respectivement. - Si
SᵢouTᵢsont des paramètres de type, laissezS₀etT₀soyez leurs classes de base effectives, sinon laissezS₀etT₀soyezSᵢetTᵢ, respectivement.
- S’il
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 composeS₀(s’ilS₀existe et est une classe ou un struct), des classes de base deS₀(s’ilS₀existe et est une classe) etT₀(s’il s’agitT₀d’une classe ou d’un struct). Un type est ajouté au jeuDuniquement 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.
UCet 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 dansDcette conversion d’un type englobantEà un type compris parT. SiUelle 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 dansU:- S’il
Sexiste et l’un des opérateurs enUconversion à partir deS,SₓestS. - Sinon,
Sₓest le type le plus englobant dans l’ensemble combiné de types sources des opérateurs dansU. Si un type le plus englobant est introuvable exactement, la conversion est ambiguë et une erreur au moment de la compilation se produit.
- S’il
Recherchez le type cible le plus spécifique,
Tₓdes opérateurs dansU:- Si l’un des opérateurs en
UconversionTest ,TₓestT. - Sinon,
Tₓest le type le plus englobant dans l’ensemble combiné de types cibles des opérateurs dansU. Si un type le plus englobant est introuvable exactement, la conversion est ambiguë et une erreur au moment de la compilation se produit.
- Si l’un des opérateurs en
Recherchez l’opérateur de conversion le plus spécifique :
- Si
Ucontient exactement un opérateur de conversion défini par l’utilisateur qui se convertitSₓenTₓ, il s’agit de l’opérateur de conversion le plus spécifique. - Sinon, s’il
Ucontient exactement un opérateur deSₓconversion levé qui se convertit enTₓ, 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.
- Si
Enfin, appliquez la conversion :
- Si E n’a pas encore le type
Sₓ, une conversion implicite standard à partir deEcelle-ciSₓ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 pasTle cas, une conversion implicite standard estTₓTeffectuée.
- Si E n’a pas encore le type
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
SetS₀T₀.- S’il
Ea un type, laissez-leSêtre. - Si
SouTsont des types valeur nullables, laissezSᵢetTᵢsoyez leurs types sous-jacents, sinon laissezSᵢetTᵢsoyezSetT, respectivement. - Si
SᵢouTᵢsont des paramètres de type, laissezS₀etT₀soyez leurs classes de base effectives, sinon laissezS₀etT₀soyezSᵢetTᵢ, respectivement.
- S’il
- 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 composeS₀(s’ilS₀existe et est une classe ou un struct), des classes deS₀base (s’ilS₀existe et est une classe),T₀(s’il s’agitT₀d’une classe ou d’un struct) et des classes de base deT₀(s’il s’agitT₀d’une classe). Un type est ajouté au jeuDuniquement 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.
UCet 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 quiDse convertissent d’un type englobantEou englobant parS(s’il existe) en un type englobant ou englobant parT. SiUelle 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 dansU:- S’il existe et l’un des opérateurs en
Uconversion à partir deS,SₓestS. - Sinon, si l’un des opérateurs en
Uconversion à partir de types qui englobentE,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 dansU. Si un type le plus englobant est introuvable exactement, la conversion est ambiguë et une erreur au moment de la compilation se produit.
- S’il existe et l’un des opérateurs en
- Recherchez le type cible le plus spécifique,
Tₓdes opérateurs dansU:- Si l’un des opérateurs en
UconversionTest ,TₓestT. - Dans le cas contraire, si l’un des opérateurs convertis
Uen types englobantsTTₓ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 dansU. Si aucun type le plus englobant n’est trouvé, la conversion est ambiguë et une erreur au moment de la compilation se produit.
- Si l’un des opérateurs en
- 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 enTₓ, il s’agit de l’opérateur de conversion le plus spécifique. - Sinon, s’il
Ucontient exactement un opérateur deSₓconversion levé qui se convertit enTₓ, 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.
- Si U contient exactement un opérateur de conversion défini par l’utilisateur qui se
- Enfin, appliquez la conversion :
- Si
Ece n’est pas déjà le typeSₓ, une conversion explicite standard d’E versSₓ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 pasTle cas, une conversion explicite standard estTₓTeffectuée.
- Si
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
Une conversion nullable permet une conversion prédéfinie qui fonctionne sur un type valeur non nullable pour être également utilisée avec la forme nullable de ce type. 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?versT? - Conversion implicite ou explicite de
SversT? - Conversion explicite de
S?versT.
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 (
HasValuepropriété),falsele résultat est la valeur Null du typeT?. - Sinon, la conversion est évaluée comme un déballage à partir de
S?, suivi de la conversion sous-jacente versSS, suivi d’un retour à l’habillage deTversT.T?
- Si la valeur source est Null (
- Si la conversion nullable est de à partir
ST?de , la conversion est évaluée en tant que conversion sous-jacente deSversTsuivie d’une habillage deTversT?. - Si la conversion nullable est de à partir
S?de , la conversion est évaluée comme un déballage à partir duquelTS?la conversion sous-jacente passeSà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.21). 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
Fcontient un anonymous_function_signature,DetFdisposez du même nombre de paramètres. - S’il
Fne contient pas de anonymous_function_signature,Dil peut avoir zéro ou plusieurs paramètres d’un type, tant qu’aucun paramètre n’est un paramètre deDsortie. - S’il
Fexiste une liste de paramètres typée explicitement, chaque paramètre présenteDles mêmes modificateurs que le paramètre correspondant etFune conversion d’identité existe entre le paramètre correspondant dansF. - Si
Fune listeDde paramètres implicitement typée n’a pas de référence ou de paramètres de sortie. - Si le corps de
Fest une expression et qu’il a un type de retourDvoid ouFqu’il est asynchrone et queDa un type de retour«TaskType»(§15.14.1), alors lorsque chaque paramètre deFreçoit le type du paramètre correspondant dansD, le corps deFest une expression valide (w.r.t §12) qui serait autorisée en tant qu’expression d’instruction (§13.7). - Si le corps d’un
Fbloc est un bloc et a un typeDest asynchrone et a unFtype de retour, alors lorsque chaque paramètre estDdonné au type du paramètre correspondant dans«TaskType», le corps d’unFbloc valide (w.r.tD) dans lequel aucune instruction neFspécifie d’expression. - Si le corps de
Fest une expression, et qu’il estFnon asynchrone et queDa un type de retour nonvoidT, ouFqu’il est asynchrone et queDa un type de retour«TaskType»<T>(§15.14.1), alors lorsque chaque paramètre deFreçoit le type du paramètre correspondant dansD, le corps deFest une expression valide (w.r.t §12) qui est implicitement convertible enT. - Si le corps d’un
Fbloc est un bloc etFqu’il n’est pas asynchrone etDa un typeTde retour non void, ouFest asynchrone etDa un«TaskType»<T>type de retour, alors lorsque chaque paramètre d’estFdonné le type du paramètre correspondant dansD, le corps d’unFbloc 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 typeAet retourne une valeur de typeR: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; // Okle 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’ellexest donnéeint,x + 1est une expression valide qui est implicitement convertible en typeint.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 + 1typeint) est implicitement convertible en typedouble.Toutefois, la troisième affectation est une erreur au moment de la compilation, car, lorsqu’elle
xest donnéedouble, le résultat (x + 1de typedouble) n’est pas implicitement convertible en typeint.La quatrième affectation convertit correctement la fonction asynchrone anonyme en type
Func<int, Task<int>>délégué, car le résultat (x + 1de typeint) est implicitement convertible en typeintde retour effectif du lambda asynchrone, qui a un typeTask<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.5) 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));
...
}
}
Puisque les deux délégués de fonctions anonymes ont le même ensemble (vide) de variables externes capturées, et puisque les fonctions anonymes sont sémantiquement identiques, un compilateur est autorisé à faire en sorte que les délégués fassent référence à la même méthode cible. 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
asyncmodificateur- 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 (§21.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
Mest sélectionnée correspondant à un appel de méthode (§12.8.10.2) du formulaireE(A), avec les modifications suivantes :- La liste
Ad’arguments est une liste d’expressions, chacune classifiée en tant que variable et avec le type et le modificateur (in,outouref) du paramètre correspondant dans la parameter_list de — à l’exception desDparamètres de typedynamic, où l’expression correspondante a le typeobjectau lieu dedynamic. - 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.
- La liste
- Une conversion est considérée comme existante si l’algorithme de §12.8.10.2 produit une méthode
Moptimale qui est compatible (§21.4) avecD. - Si la méthode sélectionnée est une méthode
Md’instance, l’expression d’instance associée détermineEl’objet cible du délégué. - Si la méthode sélectionnée est une méthode
Md’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
d1convertir implicitement le groupeFde méthodes en valeur de typeD1.L’affectation pour
d2indiquer 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
d3afficher comment aucune conversion n’existe si la méthode n’est pas applicable.Affectation pour
d4montre comment la méthode doit être applicable sous sa forme normale.Affectation pour
d5afficher 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, unSystem.NullReferenceExceptionest 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
Ddé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 (§21.5). S’il n’y a pas suffisamment de mémoire disponible pour allouer la nouvelle instance, une
System.OutOfMemoryExceptionvaleur est levée. Sinon, l’instance est initialisée avec les références données.
ECMA C# draft specification