Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Observação
Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui mudanças de especificação propostas, juntamente com as informações necessárias durante o design e desenvolvimento do recurso. Estes artigos são publicados até que as alterações de especificações 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 Language Design Meeting (LDM).
Você pode saber mais sobre o processo de adoção de especificações de recursos no padrão de linguagem C# no artigo sobre as especificações .
Questão campeã: https://github.com/dotnet/csharplang/issues/8374
Resumo
Atualizações para as melhores regras de conversão para ser mais consistente com paramse lidar melhor com os cenários de ambiguidade atuais. Por exemplo, ReadOnlySpan<string> vs ReadOnlySpan<object> pode atualmente causar ambiguidades durante a resolução de sobrecarga para [""].
Projeto Detalhado
A seguir estão as melhores conversões a partir das regras de expressão. Estas substituem as regras em https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#overload-resolution.
Estas regras são:
Dado um
C₁de conversão implícito que converte de umEde expressão para um tipoT₁, e umC₂de conversão implícito que converte de umEde expressão para um tipoT₂,C₁é umde conversãomelhor do queC₂se uma das seguintes opções se mantiver:
Eé uma expressão de coleção , eC₁é uma conversão de coleção melhor da expressão do que a coleçãoC₂Enão é uma expressão de coleção e uma das seguintes condições é verdadeira:
Ecorresponde exatamenteT₁eEnão corresponde exatamente aT₂Ecorresponde exatamente a ambos ou nenhum dosT₁eT₂, eT₁é um alvo de conversão melhor do queT₂Eé um grupo de métodos, ...
Adicionamos uma nova definição para para uma melhor conversão da coleção a partir da expressão, conforme segue:
Dado:
-
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₁ᵢsão as séries de conversões deELᵢparaE₁ -
CE₂ᵢsão as séries de conversões deELᵢparaE₂
Se houver uma conversão de identidade de E₁ para E₂, então as conversões de elementos são tão boas quanto as outras. Caso contrário, as conversões de elemento para E₁ sã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 os outros.
As comparações de conversão são feitas usando uma melhor conversão da expressão seELᵢnão for um elemento spread. SeELᵢfor um elemento spread, usaremos uma melhor conversão do tipo de elemento da coleção spread paraE₁ouE₂, respectivamente.
C₁ é uma melhor conversão de coleção de expressão do que C₂ se:
- Tanto
T₁quantoT₂não são tipos de span , eT₁é implicitamente conversível paraT₂, eT₂não é implicitamente conversível paraT₁ou -
E₁não tem uma conversão de identidade paraE₂, e as conversões de elemento paraE₁são melhores do que as conversões de elemento paraE₂, ou -
E₁tem uma conversão de identidade paraE₂e uma das seguintes é verdadeira:-
T₁éSystem.ReadOnlySpan<E₁>eT₂éSystem.Span<E₂>, ou -
T₁éSystem.ReadOnlySpan<E₁>ouSystem.Span<E₁>eT₂é um array_or_array_interface com tipo de elementoE₂
-
Caso contrário, nenhum dos tipos de recolha é melhor e o resultado é ambíguo.
Observação
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. A título de exemplo:
public void M(ReadOnlySpan<int> ros) { ... }
public void M(Span<int?> span) { ... }
M([]); // Ambiguous
Cenários:
Em inglês simples, os tipos de coleção em si devem ser os mesmos, ou inequivocamente melhores (ou seja, List<T> e List<T> são os mesmos, List<T> é inequivocamente 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 as mesmas 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₁ᵢ vs CE₂ᵢ |
Resultado |
|---|---|---|---|---|---|---|
List<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ é melhor |
List<int> é escolhida |
List<int> |
List<byte> |
[(int)1, (byte)2] |
[Identity, Implicit Numeric] |
Não aplicável |
T₂ não é aplicável |
List<int> é escolhida |
List<int> |
List<byte> |
[1, (byte)2] |
[Identity, Implicit Numeric] |
[Implicit Constant, Identity] |
Nem é melhor | Ambíguo |
List<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ é melhor |
List<byte> é escolhida |
List<int?> |
List<long> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
Nem é 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?> é escolhida |
List<short> |
List<long> |
[1, 2, 3] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ é melhor |
List<short> é escolhida |
IEnumerable<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ é melhor |
IEnumerable<int> é escolhida |
IEnumerable<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ é melhor |
List<byte> é escolhida |
int[] |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ é melhor |
int[] é escolhida |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", "", ""] |
[Identity, Identity, Identity] |
[Implicit Reference, Implicit Reference, Implicit Reference] |
CE₁ᵢ é melhor |
ReadOnlySpan<string> é escolhida |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", new object()] |
Não aplicável | [Implicit Reference, Identity] |
T₁ não é aplicável |
ReadOnlySpan<object> é escolhida |
ReadOnlySpan<object> |
Span<string> |
["", ""] |
[Implicit Reference] |
[Identity] |
CE₂ᵢ é melhor |
Span<string> é escolhida |
ReadOnlySpan<object> |
Span<string> |
[new object()] |
[Identity] |
Não aplicável |
T₁ não é aplicável |
ReadOnlySpan<object> é escolhida |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{1}"] |
[Interpolated String Handler] |
[Identity] |
CE₁ᵢ é melhor |
ReadOnlySpan<InterpolatedStringHandler> é escolhida |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{"blah"}"] |
[Interpolated String Handler] |
[Identity] - Mas constante |
CE₂ᵢ é melhor |
ReadOnlySpan<string> é escolhida |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}"] |
[Identity] |
[Interpolated String] |
CE₂ᵢ é melhor |
ReadOnlySpan<string> é escolhida |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}", (FormattableString)null] |
Não aplicável | [Interpolated String, Identity] |
T₁ não é aplicável |
ReadOnlySpan<FormattableString> é escolhida |
HashSet<short> |
Span<long> |
[1, 2] |
[Implicit Constant, Implicit Constant] |
[Implicit Numeric, Implicit Numeric] |
CE₁ᵢ é melhor |
HashSet<short> é escolhida |
HashSet<long> |
Span<short> |
[1, 2] |
[Implicit Numeric, Implicit Numeric] |
[Implicit Constant, Implicit Constant] |
CE₂ᵢ é melhor |
Span<short> é escolhida |
Perguntas abertas
Até que ponto devemos priorizar ReadOnlySpan/Span sobre outros tipos?
Tal como 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 aqui? A variante List<T> parece razoável, e subtipos de List<T> existem muitos. Mas a versão HashSet tem semânticas muito diferentes, quão certos temos de que é realmente "pior" do que ReadOnlySpan nesta API?
C# feature specifications