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 typelong
est implicite, de sorte que les expressions de typeint
peuvent implicitement être traitées comme du typelong
. La conversion opposée, du type au typelong
int
, 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
etT
, pour n’importe quel typeT
. - Entre
T
etT?
pour n’importe quel typeT
de référence . - Entre
object
etdynamic
. - 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
t1
ett2
t3
tous ont deux éléments : unint
suivi d’unstring
. Les types d’éléments Tuple peuvent eux-mêmes par des tuples, comme danst4
,t5
ett6
. 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
,t5
ett6
.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
,long
float
, ,double
, oudecimal
. - De
byte
àshort
,ushort
int
uint
long
,ulong
,float
, ,double
, ou .decimal
- De
short
àint
, ,long
,float
,double
, oudecimal
. - De
ushort
àint
, ,uint
,long
ulong
, ,float
,double
, oudecimal
. - De
int
àlong
,float
, ,double
oudecimal
. - De
uint
àlong
, ,ulong
,float
,double
, oudecimal
. - De
long
àfloat
,double
oudecimal
. - De
ulong
àfloat
,double
oudecimal
. - De
char
àushort
, ,int
,uint
long
,ulong
, ,float
,double
, oudecimal
. - De
float
àdouble
.
Les conversions de int
, vers uint
long
et ulong
vers float
long
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.FormattableString
System.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
etdynamic
. - De n’importe quel class_type à n’importe quel
S
, fourniT
est dérivé deS
.T
- De n’importe quel class_type à n’importe
T
- De n’importe quel interface_type à n’importe quel interface_type
S
, fourniT
est dérivé deS
.T
- D’un
S
avec un typeSᵢ
d’élément à unT
avec un typeTᵢ
d’élément , à condition que toutes les valeurs suivantes soient remplies :S
etT
diffèrent uniquement dans le type d’élément. En d’autres termes,S
etT
ont 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 deS
versT
. - 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₀
etT₀
a une conversion d’identité enT
. - 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
interfaceI
, 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
v
de typeS
consiste 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
S
a le typeS
d’exécution et une vérification de type runtime à l’aide de l’opérateuris
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 typeobject
moins 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
p
box
causes de la copie de la valeur.p
Au lieuPoint
de cela, la valeur 20 était déclaréeclass
, carp
ellebox
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 object
en , 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
eti
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 (d
string
) au type cible. Une conversion est trouvée enstring
mais 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.23) de type
int
peut être converti en typesbyte
, ,byte
,short
ushort
,uint
ouulong
, à 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 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 classeC
de base effective , deT
vers n’importe quelle classe de base deC
, et deT
vers n’importe quelle interface implémentée parC
. - De
T
vers un interface_typeI
dansT
's effective interface set and fromT
to any base interface ofI
. - À partir d’un paramètre de type fourni qui
T
U
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 deT
sera toujours un type référence, même s’ilU
n’est pas connu pour être un type de référence au moment deU
la 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 classeC
de base effective , deT
vers n’importe quelle classe de base deC
, et deT
vers n’importe quelle interface implémentée parC
.Remarque :
C
sera l’un des typesSystem.Object
,System.ValueType
ouSystem.Enum
(sinonT
serait connu comme un type référence). Note de fin - De
T
vers un interface_typeI
dansT
's effective interface set and fromT
to any base interface ofI
.
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 U
de . 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 T
U
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 T
de type donné :
- De
T
à un typeS
ré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 typeI
d’interface s’il a une conversion implicite en typeI₀
d’interface etI₀
est convertible enI
variance vers (§18.2.3.3). Au moment de l’exécution, s’il s’agitT
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 T
type 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 ,
t1
t2
t4
ett5
sont toutes valides, car les conversions implicites existent des expressions d’élément vers les types d’éléments correspondants. La déclaration det3
n’est pas valide, car il n’y a pas de conversion ennull
int
. La déclaration de causes d’un avertissement, car les noms d’élémentst5
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
, ouchar
. - De
byte
àsbyte
ouchar
. - De
short
àsbyte
, ,byte
,ushort
uint
, ,ulong
, ouchar
. - De
ushort
àsbyte
,byte
, ,short
ouchar
. - De
int
àsbyte
, ,byte
,short
ushort
, ,uint
,ulong
, ouchar
. - De
uint
àsbyte
, ,byte
,short
ushort
, ,int
, ouchar
. - De
long
àsbyte
, ,byte
,short
ushort
,int
, ,uint
,ulong
, ouchar
. - De
ulong
àsbyte
, ,byte
,short
ushort
,int
, ,uint
,long
, ouchar
. - De
char
àsbyte
,byte
oushort
. - De
float
àsbyte
,byte
short
ushort
, ,int
uint
long
ulong
char
, , ou .decimal
- De
double
àsbyte
,byte
short
ushort
,int
,uint
long
ulong
char
float
ou .decimal
- De
decimal
àsbyte
,byte
short
ushort
,int
,uint
long
ulong
char
float
ou .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 uneSystem.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
- Dans un
- 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, uneSystem.OverflowException
valeur est levée. - Pour une conversion depuis
float
oudouble
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.
- 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
double
versfloat
, ladouble
valeur est arrondie à la valeur la plusfloat
proche. Si ladouble
valeur 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 ladouble
valeur est trop grande pour représenter en tantfloat
que , le résultat devient infini avec le même signe que la valeur. Si ladouble
valeur est NaN, le résultat est également NaN. - Pour une conversion de
float
oudouble
versdecimal
, la valeur source est convertie endecimal
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 elledecimal
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
decimal
valeur , 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
decimal
versfloat
oudouble
, ladecimal
valeur est arrondie au plusdouble
proche 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
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 etfloat
double
, mais n’est pas garantie. Pourdecimal
les représentations sans valeurs Infinis ou NaN, et avec une plage inférieurefloat
à , le résultat d’une conversion versdecimal
l’un oufloat
l’autredouble
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
,byte
short
ushort
int
uint
long
ulong
char
float
double
oudecimal
à n’importe quel enum_type. - De n’importe quel enum_type à
sbyte
, ,byte
short
,ushort
int
uint
long
ulong
char
,float
,double
ou .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
E
avec et un type sous-jacent deint
, une conversion deE
versbyte
est traitée comme une conversion numérique explicite (§10.3.2) deint
vers , et une conversion debyte
versbyte
E
est traitée comme une conversion numérique implicite (§10.2.3) de àbyte
partir deint
. 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
S
interface_typeT
, fourniS
n’est pas scellé et fourniS
n’implémenteT
pas . - De n’importe quel interface_type
S
quel class_typeT
, fourniT
n’est pas scellé ou fourniT
implémenteS
. - De n’importe quel interface_type
S
quel interface_typeT
, fourniS
n’est pas dérivé deT
. - D’un
S
avec un typeSᵢ
d’élément à unT
avec un typeTᵢ
d’élément , à condition que toutes les valeurs suivantes soient remplies :S
etT
diffèrent uniquement dans le type d’élément. En d’autres termes,S
etT
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_type
S[]
versSystem.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 deS
versT
. - 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 deS
T. - Depuis
System.Delegate
et les interfaces qu’il implémente à n’importe quelle delegate_type. - D’un type
S
référence à un typeT
de référence s’il a une conversion de référence explicite depuisS
vers un typeT₀
de référence etT₀
qu’il existe une conversion d’identité àT₀
partir deT
. - D’un type
S
référence à une interface ou à un typeT
délégué s’il existe une conversion de référence explicite d’uneS
interface ou d’un typeT₀
délégué et estT₀
convertibleT
T
en variance-convertible enT₀
§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è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 T
type 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 leI₀
non_nullable_value et une conversion d’identité de versI
.I₀
- De n’importe quel interface_type
I
quel 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 soitI₀
variance_convertible versI
ouI
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_type
S
consiste à 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
C
de base effective deT
versT
et de n’importe quelle classe de base deC
versT
. - 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 deT
versI
. - D’un type_parameter à fournir qui
U
dépendT
(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’ilT
n’est pas connu pour être un type de référence au moment deU
la 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 deT
versT
et de n’importe quelle classe de base deC
versT
.Remarque : C sera l’un des types
System.Object
,System.ValueType
ouSystem.Enum
(sinonT
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 deT
versI
. Cette conversion se compose d’une conversion de boxe implicite (§10.2.9) àT
partir d’uneobject
conversion de référence explicite deobject
versI
. Au moment de l’exécution, s’il s’agitT
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’agitT
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 quiT
dépendU
(§15.2.5). Au moment de l’exécution, s’il s’agitT
d’un type valeur etU
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 deuxT
types sontU
des types valeur, etT
U
sont nécessairement le même type et qu’aucune conversion n’est effectuée. Au moment de l’exécution, s’il s’agitT
d’un type référence, ilU
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
t
long
é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 boxedint
ne 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
A
en typeB
, une conversion explicite standard existe de typeA
enB
type et de typeB
en 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 S
T
cible 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 S
T
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
S
ouT
depuisT
versS
.
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 T
S
, puis exécute une conversion définie par l’utilisateur à X
partir 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
A
B
, et si aucun n’estA
interface_typeB
s
, ilA
est dit être englobant parB
, etB
est dit englober .A
- Si une conversion implicite standard (§10.4.2) existe d’une expression
E
en un typeB
, et si aucunB
ni le type deE
(s’il en a un) ne sont interface_types
, ilE
est dit êtreB
, etB
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
S
etS₀
T₀
.- S’il
E
a un type, laissez-leS
être. - Si
S
ouT
sont des types valeur nullables, laissezSᵢ
etTᵢ
soyez leurs types sous-jacents, sinon laissezSᵢ
etTᵢ
soyezS
etT
, 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 jeuD
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 dansD
cette conversion d’un type englobantE
à un type compris parT
. SiU
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 enU
conversion à 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
U
conversionT
est ,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
U
contient 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
U
contient 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 deE
celle-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 pasT
le cas, une conversion implicite standard estTₓ
T
effectué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
S
etS₀
T₀
.- S’il
E
a un type, laissez-leS
être. - Si
S
ouT
sont des types valeur nullables, laissezSᵢ
etTᵢ
soyez leurs types sous-jacents, sinon laissezSᵢ
etTᵢ
soyezS
etT
, 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).A
le type est ajouté au jeuD
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 quiD
se convertissent d’un type englobantE
ou englobant parS
(s’il existe) en un type englobant ou englobant parT
. SiU
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 dansU
:- S’il existe et l’un des opérateurs en
U
conversion à partir deS
,Sₓ
estS
. - Sinon, si l’un des opérateurs en
U
conversion à 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
U
conversionT
est ,Tₓ
estT
. - Dans le cas contraire, si l’un des opérateurs convertis
U
en types englobantsT
Tₓ
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
U
contient 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
E
ce 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 pasT
le cas, une conversion explicite standard estTₓ
T
effectué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
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?
versT?
- Conversion implicite ou explicite de
S
versT?
- 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 S
T
produit suivant :
- Si la conversion nullable est de
S?
:T?
- Si la valeur source est Null (
HasValue
propriété),false
le 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 versS
S
, suivi d’un retour à l’habillage deT
versT
.T?
- Si la valeur source est Null (
- Si la conversion nullable est de à partir
S
T?
de , la conversion est évaluée en tant que conversion sous-jacente deS
versT
suivie d’une habillage deT
versT?
. - Si la conversion nullable est de à partir
S?
de , la conversion est évaluée comme un déballage à partir duquelT
S?
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 T
valeur 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 S
S
être suivie d’un retour T
T
à 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
etF
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 deD
sortie. - S’il
F
existe une liste de paramètres typée explicitement, chaque paramètre présenteD
les mêmes modificateurs que le paramètre correspondant etF
une conversion d’identité existe entre le paramètre correspondant dansF
. - Si
F
une listeD
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 etD
a un type de retour void ouF
est asynchrone etD
a un«TaskType»
type de retour (§15.15.1), alors quand chaque paramètre deF
est donné le type du paramètre correspondant dansD
, le corpsF
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 etD
a un typeF
est asynchrone etD
a un«TaskType»
type de retour, alors lorsque chaque paramètre estF
donné au type du paramètre correspondant dansD
, le corps d’unF
bloc valide (w.r.t §13.3) dans lequel aucune instruction nereturn
spécifie d’expression. - Si le corps d’une
F
expression est une expression, etF
n’est pas asynchrone etD
qu’il a un typevoid
de non-retourT
, ouF
qu’ilD
a un«TaskType»<T>
type de retour (§15.15.1), lorsque chaque paramètre duF
paramètre correspondant est donné au type du paramètre correspondant, le corps d’uneD
expression valide (w.r.tF
) qui est implicitement convertible enT
. - Si le corps d’un
F
bloc est un bloc etF
qu’il n’est pas asynchrone etD
a un typeT
de retour non void, ouF
est asynchrone etD
a un«TaskType»<T>
type de retour, alors lorsque chaque paramètre d’estF
donné le type du paramètre correspondant dansD
, le corps d’unF
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 typeA
et 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; // 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’ellex
est donnéeint
,x + 1
est 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 + 1
typeint
) est implicitement convertible en typedouble
.Toutefois, la troisième affectation est une erreur au moment de la compilation, car, lorsqu’elle
x
est donnéedouble
, le résultat (x + 1
de 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 + 1
de typeint
) est implicitement convertible en typeint
de 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 D
dé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 formulaireE(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
,out
ouref
) du paramètre correspondant dans la parameter_list de — à l’exception desD
paramètres de typedynamic
, où l’expression correspondante a le typeobject
au 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 étant existante si l’algorithme de §12.8.10.2
D
- Si la méthode sélectionnée est une méthode
M
d’instance, l’expression d’instance associée détermineE
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 groupeF
de méthodes en valeur de typeD1
.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
, unSystem.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.
ECMA C# draft specification