Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Nota:
Este artículo es una especificación de características. La especificación actúa como documento de diseño de la característica. Incluye cambios de especificación propuestos, junto con la información necesaria durante el diseño y el desarrollo de la característica. Estos artículos se publican hasta que se finalizan los cambios de especificación propuestos y se incorporan en la especificación ECMA actual.
Puede haber algunas discrepancias entre la especificación de características y la implementación completada. Esas diferencias se recogen en las notas de la reunión de diseño de idioma (LDM) pertinentes.
Puede obtener más información sobre el proceso de adopción de especificaciones de características en el estándar del lenguaje C# en el artículo sobre las especificaciones.
Problema planteado por el experto: https://github.com/dotnet/csharplang/issues/8374
Resumen
Actualizaciones de las mejores reglas de conversión para ser más coherentes con paramsy controlar mejor los escenarios de ambigüedad actuales. Por ejemplo, ReadOnlySpan<string> vs ReadOnlySpan<object> puede causar ambigüedades actualmente durante la resolución de sobrecarga para [""].
Diseño detallado
A continuación se muestran las mejores conversiones de las reglas de expresión. Reemplazan las reglas de https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#overload-resolution.
Estas reglas son:
Dada una conversión implícita
C₁que convierte de una expresiónEa un tipoT₁, y una conversión implícitaC₂que convierte de una expresiónEa un tipoT₂,C₁es una conversión mejor queC₂si se cumple una de las siguientes condiciones:
Ees una expresión de colección yC₁es una mejor conversión de colección de la expresión queC₂Eno es una expresión de colección y una de las siguientes suspensiones:Ees un grupo de métodos, ...
Agregamos una nueva definición para una mejor conversión de colección de la expresión, como se indica a continuación:
Con estas premisas:
-
Ees una expresión de colección con expresiones de elemento[EL₁, EL₂, ..., ELₙ] -
T₁yT₂son tipos de colección -
E₁es el tipo de elemento deT₁ -
E₂es el tipo de elemento deT₂ -
CE₁ᵢson la serie de conversiones deELᵢaE₁ -
CE₂ᵢson la serie de conversiones deELᵢaE₂
Si hay una conversión de identidad de E₁ a E₂, las conversiones de elementos son tan buenas entre sí. De lo contrario, las conversiones de elementos a E₁ son mejores que las conversiones de elementos a E₂ si:
- Para cada
ELᵢ,CE₁ᵢes al menos tan bueno comoCE₂ᵢ, y - Hay al menos un i donde
CE₁ᵢes mejor queCE₂ᵢDe lo contrario, ninguno de los conjuntos de conversiones de elementos es mejor que el otro, y tampoco son tan buenos como los unos a los otros.
Las comparaciones de conversión se realizan mediante una mejor conversión de la expresión siELᵢno es un elemento de propagación. SiELᵢes un elemento de propagación, usamos una mejor conversión del tipo de elemento de la colección de propagación aE₁oE₂, respectivamente.
C₁ es una mejor conversión de colección de la expresión que C₂ si:
- Ambos
T₁yT₂no son tipos de intervalo, yT₁se convierte implícitamente enT₂yT₂no se puede convertir implícitamente enT₁, o -
E₁no tiene una conversión de identidad aE₂y las conversiones de elementos aE₁son mejores que las conversiones de elementos enE₂, o -
E₁tiene una conversión de identidad aE₂y una de las siguientes suspensiones:-
T₁esSystem.ReadOnlySpan<E₁>, yT₂esSystem.Span<E₂>o -
T₁esSystem.ReadOnlySpan<E₁>oSystem.Span<E₁>, yT₂es un array_or_array_interface con tipo de elementoE₂
-
De lo contrario, ninguno de los tipos de colección es mejor y el resultado es ambiguo.
Nota:
Estas reglas significan que los métodos que exponen sobrecargas que toman tipos de elementos diferentes y sin una conversión entre los tipos de colección son ambiguos para las expresiones de colección vacías. Por ejemplo:
public void M(ReadOnlySpan<int> ros) { ... }
public void M(Span<int?> span) { ... }
M([]); // Ambiguous
Escenarios:
En inglés sin formato, los propios tipos de colección deben ser iguales o no ambigüedades (es decir, List<T> y List<T> son iguales, List<T> es mejor que IEnumerable<T>y no se pueden comparar) y List<T>HashSet<T> las conversiones de elementos para el mejor tipo de colección también deben ser iguales o mejores (es decir, no podemos decidir entre ReadOnlySpan<object> y Span<string> para [""], el usuario debe tomar esa decisión). Más ejemplos de esto son:
T₁ |
T₂ |
E |
C₁ Conversiones |
C₂ Conversiones |
CE₁ᵢ frente a CE₂ᵢ |
Resultado |
|---|---|---|---|---|---|---|
List<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ es mejor |
List<int> se selecciona |
List<int> |
List<byte> |
[(int)1, (byte)2] |
[Identity, Implicit Numeric] |
No aplicable |
T₂ no es aplicable |
List<int> se selecciona |
List<int> |
List<byte> |
[1, (byte)2] |
[Identity, Implicit Numeric] |
[Implicit Constant, Identity] |
Tampoco es mejor | Ambigua |
List<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ es mejor |
List<byte> se selecciona |
List<int?> |
List<long> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
Tampoco es mejor | Ambigua |
List<int?> |
List<ulong> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ es mejor |
List<int?> se selecciona |
List<short> |
List<long> |
[1, 2, 3] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ es mejor |
List<short> se selecciona |
IEnumerable<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ es mejor |
IEnumerable<int> se selecciona |
IEnumerable<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ es mejor |
List<byte> se selecciona |
int[] |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ es mejor |
int[] se selecciona |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", "", ""] |
[Identity, Identity, Identity] |
[Implicit Reference, Implicit Reference, Implicit Reference] |
CE₁ᵢ es mejor |
ReadOnlySpan<string> se selecciona |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", new object()] |
No aplicable | [Implicit Reference, Identity] |
T₁ no es aplicable |
ReadOnlySpan<object> se selecciona |
ReadOnlySpan<object> |
Span<string> |
["", ""] |
[Implicit Reference] |
[Identity] |
CE₂ᵢ es mejor |
Span<string> se selecciona |
ReadOnlySpan<object> |
Span<string> |
[new object()] |
[Identity] |
No aplicable |
T₁ no es aplicable |
ReadOnlySpan<object> se selecciona |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{1}"] |
[Interpolated String Handler] |
[Identity] |
CE₁ᵢ es mejor |
ReadOnlySpan<InterpolatedStringHandler> se selecciona |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{"blah"}"] |
[Interpolated String Handler] |
[Identity] - Pero constante |
CE₂ᵢ es mejor |
ReadOnlySpan<string> se selecciona |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}"] |
[Identity] |
[Interpolated String] |
CE₂ᵢ es mejor |
ReadOnlySpan<string> se selecciona |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}", (FormattableString)null] |
No aplicable | [Interpolated String, Identity] |
T₁ no es aplicable |
ReadOnlySpan<FormattableString> se selecciona |
HashSet<short> |
Span<long> |
[1, 2] |
[Implicit Constant, Implicit Constant] |
[Implicit Numeric, Implicit Numeric] |
CE₁ᵢ es mejor |
HashSet<short> se selecciona |
HashSet<long> |
Span<short> |
[1, 2] |
[Implicit Numeric, Implicit Numeric] |
[Implicit Constant, Implicit Constant] |
CE₂ᵢ es mejor |
Span<short> se selecciona |
Preguntas abiertas
¿Hasta dónde deberíamos priorizar ReadOnlySpan/Span otros tipos?
Como se especifica hoy, las siguientes sobrecargas serían ambiguas:
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> {}
¿Hasta qué punto queremos ir aquí? La List<T> variante parece razonable y los subtipos de List<T> existen aplenty. Pero la HashSet versión tiene una semántica muy diferente, ¿cómo estamos seguros de que es realmente "peor" que ReadOnlySpan en esta API?
C# feature specifications