Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de changer d’annuaire.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer d’annuaire.
Remarque
Cet article est une spécification de fonctionnalité. La spécification sert de document de conception pour la fonctionnalité. Elle inclut les changements de spécification proposés, ainsi que les informations nécessaires à la conception et au développement de la fonctionnalité. Ces articles sont publiés jusqu'à ce que les changements proposés soient finalisés et incorporés dans la spécification ECMA actuelle.
Il peut y avoir des divergences entre la spécification de la fonctionnalité et l'implémentation réalisée. Ces différences sont capturées dans les notes de réunion de conception de langage (LDM) pertinentes .
Vous pouvez en savoir plus sur le processus d’adoption des speclets de fonctionnalités dans la norme de langage C# dans l’article sur les spécifications.
Problème de champion : https://github.com/dotnet/csharplang/issues/8374
Résumé
Mises à jour vers les meilleures règles de conversion pour être plus cohérentes avec params, et mieux gérer les scénarios d’ambiguïté actuels. Par exemple, ReadOnlySpan<string> vs ReadOnlySpan<object> peut actuellement provoquer des ambiguïtés pendant la résolution de surcharge pour [""].
Conception détaillée
Voici la meilleure conversion à partir de règles d’expression. Celles-ci remplacent les règles dans https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#overload-resolution.
Il s’agit des règles suivantes :
Étant donné une conversion implicite
C₁qui convertit d’une expressionEen typeT₁, et une conversion impliciteC₂qui convertit d’une expressionEen typeT₂,C₁est une meilleure conversion queC₂si l’une des opérations suivantes contient :
Eest une expression de collection etC₁est une meilleure conversion de collection à partir d’une expression queC₂En'est pas une expression de collection et l'une des conditions suivantes est remplie :
Ecorrespond exactement àT₁etEne correspond pas exactement auxT₂Ecorrespond exactement soit à la fois àT₁et àT₂, soit ni à l'un ni à l'autre, etT₁est une meilleure cible de conversion queT₂.Eest un groupe de méthodes, ...
Nous ajoutons une nouvelle définition pour une meilleure conversion de collection à partir d’une expression, comme suit :
Soit :
-
Eest une expression de collection avec des expressions d’élément[EL₁, EL₂, ..., ELₙ] -
T₁etT₂sont des types de collection -
E₁est le type d’élément deT₁ -
E₂est le type d’élément deT₂ -
CE₁ᵢsont les séries de conversions deELᵢversE₁ -
CE₂ᵢsont les séries de conversions deELᵢversE₂
S’il existe une conversion d’identité à E₁partir de E₂ , les conversions d’éléments sont aussi bonnes que les autres. Dans le cas contraire, les conversions d’éléments sont E₁meilleures que les conversions d’éléments si E₂ :
- Pour chaque
ELᵢ,CE₁ᵢest au moins aussi bon queCE₂ᵢ, et - Il y a au moins un i où
CE₁ᵢest meilleur queCE₂ᵢSinon, aucun ensemble de conversions d’éléments n’est meilleur que l’autre, et ils ne sont pas aussi bons que les autres.
Les comparaisons de conversion sont effectuées à l’aide d’une meilleure conversion à partir d’une expression siELᵢce n’est pas un élément de propagation. S’ilELᵢs’agit d’un élément de propagation, nous utilisons une meilleure conversion du type d’élément de la collection de propagation versE₁ouE₂, respectivement.
C₁ est une meilleure conversion de collection à partir d’une expression que C₂ si :
- Les deux
T₁etT₂ne sont pas des types d’étendues, etT₁sont implicitement convertibles enT₂, etT₂n’est pas implicitement convertible enT₁, ou -
E₁n’a pas de conversion d’identité enE₂, et les conversions d’éléments àE₁sont meilleures que les conversions d’éléments enE₂, ou -
E₁a une conversion d’identité enE₂, et l’une des conservations suivantes :-
T₁estSystem.ReadOnlySpan<E₁>, etT₂estSystem.Span<E₂>, ou -
T₁estSystem.ReadOnlySpan<E₁>ouSystem.Span<E₁>, etT₂est un array_or_array_interface avec le type d’élémentE₂
-
Sinon, aucun type de collection n’est préférable et le résultat est ambigu.
Remarque
Ces règles signifient que les méthodes qui exposent des surcharges qui prennent différents types d’éléments et sans conversion entre les types de collection sont ambiguës pour les expressions de collection vides. Par exemple :
public void M(ReadOnlySpan<int> ros) { ... }
public void M(Span<int?> span) { ... }
M([]); // Ambiguous
Scenarios :
En anglais brut, les types de collection eux-mêmes doivent être identiques, ou sans ambiguïté (c’est-à-dire, List<T> et List<T> sont identiques, List<T> est clairement mieux que IEnumerable<T>, et List<T> ne peuvent pas être comparés), et HashSet<T> les conversions d’éléments pour le meilleur type de collection doivent également être identiques ou meilleures (c’est-à-dire, nous ne pouvons pas décider entre ReadOnlySpan<object> et Span<string> pour [""], l’utilisateur doit prendre cette décision). Voici d’autres exemples :
T₁ |
T₂ |
E |
C₁ Conversions |
C₂ Conversions |
Comparaison de CE₁ᵢ et de CE₂ᵢ |
Résultat |
|---|---|---|---|---|---|---|
List<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ est mieux |
List<int> est choisi |
List<int> |
List<byte> |
[(int)1, (byte)2] |
[Identity, Implicit Numeric] |
Sans objet |
T₂ n’est pas applicable |
List<int> est choisi |
List<int> |
List<byte> |
[1, (byte)2] |
[Identity, Implicit Numeric] |
[Implicit Constant, Identity] |
Ni l’un ni l’autre n’est | Ambigu |
List<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ est mieux |
List<byte> est choisi |
List<int?> |
List<long> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
Ni l’un ni l’autre n’est | Ambigu |
List<int?> |
List<ulong> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ est mieux |
List<int?> est choisi |
List<short> |
List<long> |
[1, 2, 3] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ est mieux |
List<short> est choisi |
IEnumerable<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ est mieux |
IEnumerable<int> est choisi |
IEnumerable<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ est mieux |
List<byte> est choisi |
int[] |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ est mieux |
int[] est choisi |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", "", ""] |
[Identity, Identity, Identity] |
[Implicit Reference, Implicit Reference, Implicit Reference] |
CE₁ᵢ est mieux |
ReadOnlySpan<string> est choisi |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", new object()] |
Sans objet | [Implicit Reference, Identity] |
T₁ n’est pas applicable |
ReadOnlySpan<object> est choisi |
ReadOnlySpan<object> |
Span<string> |
["", ""] |
[Implicit Reference] |
[Identity] |
CE₂ᵢ est mieux |
Span<string> est choisi |
ReadOnlySpan<object> |
Span<string> |
[new object()] |
[Identity] |
Sans objet |
T₁ n’est pas applicable |
ReadOnlySpan<object> est choisi |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{1}"] |
[Interpolated String Handler] |
[Identity] |
CE₁ᵢ est mieux |
ReadOnlySpan<InterpolatedStringHandler> est choisi |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{"blah"}"] |
[Interpolated String Handler] |
[Identity] - Mais constante |
CE₂ᵢ est mieux |
ReadOnlySpan<string> est choisi |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}"] |
[Identity] |
[Interpolated String] |
CE₂ᵢ est mieux |
ReadOnlySpan<string> est choisi |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}", (FormattableString)null] |
Sans objet | [Interpolated String, Identity] |
T₁ n’est pas applicable |
ReadOnlySpan<FormattableString> est choisi |
HashSet<short> |
Span<long> |
[1, 2] |
[Implicit Constant, Implicit Constant] |
[Implicit Numeric, Implicit Numeric] |
CE₁ᵢ est mieux |
HashSet<short> est choisi |
HashSet<long> |
Span<short> |
[1, 2] |
[Implicit Numeric, Implicit Numeric] |
[Implicit Constant, Implicit Constant] |
CE₂ᵢ est mieux |
Span<short> est choisi |
Questions ouvertes
Jusqu’à quel point devons-nous hiérarchiser ReadOnlySpan/Span les autres types ?
Comme indiqué aujourd’hui, les surcharges suivantes seraient ambiguës :
C.M1(["Hello world"]); // Ambiguous, no tiebreak between ROS and List
C.M2(["Hello world"]); // Ambiguous, no tiebreak between Span and List
C.M3(["Hello world"]); // Ambiguous, no tiebreak between ROS and MyList.
C.M4(["Hello", "Hello"]); // Ambiguous, no tiebreak between ROS and HashSet. Created collections have different contents
class C
{
public static void M1(ReadOnlySpan<string> ros) {}
public static void M1(List<string> list) {}
public static void M2(Span<string> ros) {}
public static void M2(List<string> list) {}
public static void M3(ReadOnlySpan<string> ros) {}
public static void M3(MyList<string> list) {}
public static void M4(ReadOnlySpan<string> ros) {}
public static void M4(HashSet<string> hashset) {}
}
class MyList<T> : List<T> {}
Jusqu’à quel point voulons-nous aller ici ? La List<T> variante semble raisonnable et sous-types d’existence List<T> aplentie. Mais la HashSet version a une sémantique très différente, comment sommes-nous sûrs que c’est réellement « pire » que ReadOnlySpan dans cette API ?
C# feature specifications