Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Nota
Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui alterações de especificação propostas, juntamente com as informações necessárias durante o design e o desenvolvimento do recurso. Esses artigos são publicados até que as alterações de especificação propostas sejam finalizadas e incorporadas na especificação ECMA atual.
Pode haver algumas discrepâncias entre a especificação do recurso e a implementação concluída. Essas diferenças são capturadas nas notas pertinentes da reunião de design de idioma (LDM).
Você pode aprender mais sobre o processo de adoção de especificações compactas de recursos no padrão da linguagem C# no artigo sobre as especificações de .
Problema do especialista: https://github.com/dotnet/csharplang/issues/8374
Resumo
Atualiza as melhores regras de conversão para serem mais consistentes com o paramse lidar de forma mais eficaz com os atuais cenários de ambiguidade. Por exemplo, a comparação entre ReadOnlySpan<string> e ReadOnlySpan<object> no momento pode causar ambiguidades durante a resolução de sobrecarga para [""].
Design detalhado
A seguir, as melhores conversões das regras de expressões. Elas substituem as regras no https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#overload-resolution.
Estas regras são:
Considerando uma conversão implícita
C₁que converte de uma expressãoEpara um tipoT₁, e uma conversão implícitaC₂que converte de uma expressãoEpara um tipoT₂,C₁é uma conversão melhor do queC₂se uma das seguintes condições:
Eé uma expressão de coleção , eC₁é uma conversão de coleção melhor da expressão do queC₂.Enão é uma expressão de coleção e uma das seguintes retenções:
Ecorresponde exatamente aT₁eEnão corresponde exatamente aT₂Ecorresponde exatamente a ambos ou a nenhum deT₁eT₂, eT₁é um alvo de conversão melhor do queT₂Eé um grupo de métodos, ...
Adicionamos uma nova definição para visando a melhor conversão de coleção da expressão, da seguinte maneira:
Considerando:
-
Eé uma expressão de coleção com expressões de elemento[EL₁, EL₂, ..., ELₙ] -
T₁eT₂são tipos de coleção -
E₁é o tipo de elemento deT₁ -
E₂é o tipo de elemento deT₂ -
CE₁ᵢé a série de conversões deELᵢparaE₁ -
CE₂ᵢé a série de conversões deELᵢparaE₂
Se houver uma conversão de identidade de E₁ para E₂, as conversões de elementos serão tão boas quanto as outras. Caso contrário, as conversões de elemento em E₁ serão melhores do que as conversões de elemento para E₂ se:
- Para cada
ELᵢ,CE₁ᵢé pelo menos tão bom quantoCE₂ᵢe - Há pelo menos um i onde
CE₁ᵢé melhor do queCE₂ᵢCaso contrário, nenhum conjunto de conversões de elementos é melhor do que o outro, e eles também não são tão bons quanto uns aos outros.
Comparações de conversão são feitas usando uma conversão melhor da expressão seELᵢnão for um elemento de propagação. SeELᵢfor um elemento de distribuição, usaremos uma conversão melhor do tipo de elemento da coleção de spread paraE₁ouE₂, respectivamente.
C₁ é uma conversão de coleção melhor da expressão do que C₂ se:
- Tanto
T₁quantoT₂não são tipos de intervalo, eT₁é implicitamente conversível paraT₂, eT₂não é implicitamente conversível paraT₁, ou seja, -
E₁não tem uma conversão de identidade paraE₂e as conversões de elemento emE₁são melhores do que as conversões de elemento paraE₂ou -
E₁tem uma conversão de identidade paraE₂, e uma das seguintes condições se aplica:-
T₁éSystem.ReadOnlySpan<E₁>eT₂éSystem.Span<E₂>ou -
T₁éSystem.ReadOnlySpan<E₁>ouSystem.Span<E₁>eT₂é um array_or_array_interface com o tipo de elementoE₂
-
Caso contrário, nenhum tipo de coleção é melhor e o resultado é ambíguo.
Nota
Essas regras significam que os métodos que expõem sobrecargas que usam tipos de elementos diferentes e sem uma conversão entre os tipos de coleção são ambíguos para expressões de coleção vazias. Como exemplo:
public void M(ReadOnlySpan<int> ros) { ... }
public void M(Span<int?> span) { ... }
M([]); // Ambiguous
Cenários:
Em inglês sem formatação, os próprios tipos de coleção devem ser iguais, ou inequívocamente melhor (ou seja, List<T> e List<T> são iguais, List<T> é inequívocamente melhor do que IEnumerable<T>, e List<T> e HashSet<T> não podem ser comparados), e as conversões de elementos para o melhor tipo de coleção também devem ser iguais ou melhores (ou seja, não podemos decidir entre ReadOnlySpan<object> e Span<string> para [""], o usuário precisa tomar essa decisão). Mais exemplos disso são:
T₁ |
T₂ |
E |
C₁ Conversões |
C₂ Conversões |
CE₁ᵢ versus CE₂ᵢ |
Resultado |
|---|---|---|---|---|---|---|
List<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ é melhor |
List<int> é escolhido |
List<int> |
List<byte> |
[(int)1, (byte)2] |
[Identity, Implicit Numeric] |
Não aplicável |
T₂ não é aplicável |
List<int> é escolhido |
List<int> |
List<byte> |
[1, (byte)2] |
[Identity, Implicit Numeric] |
[Implicit Constant, Identity] |
Nenhum dos dois é melhor | Ambíguo |
List<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ é melhor |
List<byte> é escolhido |
List<int?> |
List<long> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
Nenhum dos dois é melhor | Ambíguo |
List<int?> |
List<ulong> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ é melhor |
List<int?> é escolhido |
List<short> |
List<long> |
[1, 2, 3] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ é melhor |
List<short> é escolhido |
IEnumerable<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ é melhor |
IEnumerable<int> é escolhido |
IEnumerable<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ é melhor |
List<byte> é escolhido |
int[] |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ é melhor |
int[] é escolhido |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", "", ""] |
[Identity, Identity, Identity] |
[Implicit Reference, Implicit Reference, Implicit Reference] |
CE₁ᵢ é melhor |
ReadOnlySpan<string> é escolhido |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", new object()] |
Não aplicável | [Implicit Reference, Identity] |
T₁ não é aplicável |
ReadOnlySpan<object> é escolhido |
ReadOnlySpan<object> |
Span<string> |
["", ""] |
[Implicit Reference] |
[Identity] |
CE₂ᵢ é melhor |
Span<string> é escolhido |
ReadOnlySpan<object> |
Span<string> |
[new object()] |
[Identity] |
Não aplicável |
T₁ não é aplicável |
ReadOnlySpan<object> é escolhido |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{1}"] |
[Interpolated String Handler] |
[Identity] |
CE₁ᵢ é melhor |
ReadOnlySpan<InterpolatedStringHandler> é escolhido |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{"blah"}"] |
[Interpolated String Handler] |
[Identity] - mas constante |
CE₂ᵢ é melhor |
ReadOnlySpan<string> é escolhido |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}"] |
[Identity] |
[Interpolated String] |
CE₂ᵢ é melhor |
ReadOnlySpan<string> é escolhido |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}", (FormattableString)null] |
Não aplicável | [Interpolated String, Identity] |
T₁ não é aplicável |
ReadOnlySpan<FormattableString> é escolhido |
HashSet<short> |
Span<long> |
[1, 2] |
[Implicit Constant, Implicit Constant] |
[Implicit Numeric, Implicit Numeric] |
CE₁ᵢ é melhor |
HashSet<short> é escolhido |
HashSet<long> |
Span<short> |
[1, 2] |
[Implicit Numeric, Implicit Numeric] |
[Implicit Constant, Implicit Constant] |
CE₂ᵢ é melhor |
Span<short> é escolhido |
Perguntas abertas
Até que ponto devemos priorizar ReadOnlySpan/Span em relação a outros tipos?
Conforme especificado hoje, as seguintes sobrecargas seriam ambíguas:
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> {}
Até onde queremos ir? A variante List<T> parece razoável, e os subtipos de List<T> existem em abundância. Mas a versão HashSet tem semântica muito diferente, quão certo temos de que é realmente "pior" do que ReadOnlySpan nesta API?
C# feature specifications