Notes
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.
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 expressionE
en typeT₁
, et une conversion impliciteC₂
qui convertit d’une expressionE
en typeT₂
,C₁
est une meilleure conversion queC₂
si l’une des opérations suivantes contient :
E
est une expression de collection etC₁
est une meilleure conversion de collection à partir d’une expression queC₂
E
n'est pas une expression de collection et l'une des conditions suivantes est remplie :
E
correspond exactement àT₁
etE
ne correspond pas exactement auxT₂
E
correspond exactement soit à la fois àT₁
et àT₂
, soit ni à l'un ni à l'autre, etT₁
est une meilleure cible de conversion queT₂
.E
est un groupe de méthodes, ...
Nous ajoutons une nouvelle définition pour une meilleure conversion de collection à partir d’une expression, comme suit :
Soit :
-
E
est 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