Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Nota
Questo articolo è una specifica di funzionalità. La specifica funge da documento di progettazione per la funzionalità. Include le modifiche specifiche proposte, insieme alle informazioni necessarie durante la progettazione e lo sviluppo della funzionalità. Questi articoli vengono pubblicati fino a quando le modifiche specifiche proposte non vengono completate e incorporate nella specifica ECMA corrente.
Potrebbero verificarsi alcune discrepanze tra la specifica di funzionalità e l'implementazione completata. Le differenze sono documentate nelle note pertinenti del meeting di progettazione del linguaggio (LDM) .
Puoi trovare ulteriori informazioni sul processo per l'adozione di speclets di funzionalità nello standard del linguaggio C# nell'articolo sulle specifiche .
Problema del campione: https://github.com/dotnet/csharplang/issues/8374
Sommario
Gli aggiornamenti alle regole di conversione migliori sono più coerenti con paramse gestiscono meglio gli scenari di ambiguità correnti. Ad esempio, ReadOnlySpan<string> vs ReadOnlySpan<object> possono attualmente causare ambiguità durante la risoluzione dell'overload per [""].
Progettazione dettagliata
Di seguito sono riportate le migliori conversioni derivanti da regole di espressione. Queste sostituiscono le regole in https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#overload-resolution.
Queste regole sono:
Data una conversione implicita
C₁che converte un'espressioneEin un tipoT₁e una conversione implicitaC₂che converte un'espressioneEin un tipoT₂,C₁è una conversione migliore rispetto aC₂se si verifica una delle seguenti condizioni:
Eè espressione di raccolta eC₁è una conversione di raccolta migliore da espressione rispetto aC₂Enon è un'espressione di raccolta e si verifica una delle seguenti condizioni:
Ecorrisponde esattamenteT₁eEnon corrisponde esattamenteT₂Ecorrisponde esattamente aT₁eT₂eT₁è undi destinazione di conversionemigliore diT₂Eè un gruppo di metodi, ...
Aggiungiamo una nuova definizione per per una migliore conversione della raccolta dall'espressione, come indicato di seguito:
Dedito:
-
Eè un'espressione di raccolta con espressioni degli elementi[EL₁, EL₂, ..., ELₙ] -
T₁eT₂sono tipi di raccolta -
E₁è il tipo di elemento diT₁ -
E₂è il tipo di elemento diT₂ -
CE₁ᵢsono la serie di conversioni daELᵢaE₁ -
CE₂ᵢsono la serie di conversioni daELᵢaE₂
Se è presente una conversione di identità da E₁ a E₂, le conversioni degli elementi sono uguali tra loro. In caso contrario, le conversioni degli elementi in E₁ sono migliori delle conversioni degli elementi in E₂ se:
- Per ogni
ELᵢ,CE₁ᵢè almeno buono quantoCE₂ᵢe - C'è almeno un i dove
CE₁ᵢè meglio diCE₂ᵢAltrimenti, nessun set di conversioni di elementi è migliore dell'altro, e non sono anche buono come l'altro.
I confronti di conversione vengono eseguiti usando una conversione migliore dall'espressione seELᵢnon è un elemento spread. SeELᵢè un elemento spread, usiamo una conversione migliore dal tipo di elemento della raccolta spread aE₁oE₂, rispettivamente.
C₁ è una conversione migliore della raccolta dall'espressione rispetto a C₂ se:
- Sia
T₁cheT₂non sono tipi di intervallo associati a , mentreT₁è convertibile implicitamente inT₂eT₂non lo è inT₁, oppure -
E₁non dispone di una conversione di identità inE₂e le conversioni degli elementi inE₁sono migliori rispetto alle conversioni di elementi inE₂o -
E₁ha una conversione di identità inE₂e vale una delle seguenti:-
T₁èSystem.ReadOnlySpan<E₁>eT₂èSystem.Span<E₂>o -
T₁èSystem.ReadOnlySpan<E₁>oSystem.Span<E₁>eT₂è un array_or_array_interface con tipo di elementoE₂
-
In caso contrario, nessun tipo di raccolta è migliore e il risultato è ambiguo.
Nota
Queste regole stabiliscono che i metodi che offrono sovraccarichi accettando diversi tipi di elementi e senza una conversione tra i tipi di raccolta risultano ambigui per espressioni di raccolta vuote. Ad esempio:
public void M(ReadOnlySpan<int> ros) { ... }
public void M(Span<int?> span) { ... }
M([]); // Ambiguous
Scenari:
In inglese normale, I tipi di raccolta stessi devono essere uguali o non ambigui (ad esempio, List<T> e List<T> sono uguali, List<T> è preferibile in modo univoco rispetto a IEnumerable<T>e List<T> e HashSet<T> non possono essere confrontati), e le conversioni degli elementi per il tipo di raccolta migliore devono essere uguali o migliori (ad esempio, non è possibile decidere tra ReadOnlySpan<object> e Span<string> per [""], l'utente deve prendere tale decisione. Altri esempi sono:
T₁ |
T₂ |
E |
conversioni C₁ |
conversioni C₂ |
CE₁ᵢ e CE₂ᵢ |
Risultato |
|---|---|---|---|---|---|---|
List<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ è meglio |
List<int> viene selezionato |
List<int> |
List<byte> |
[(int)1, (byte)2] |
[Identity, Implicit Numeric] |
Non applicabile |
T₂ non è applicabile |
List<int> viene selezionato |
List<int> |
List<byte> |
[1, (byte)2] |
[Identity, Implicit Numeric] |
[Implicit Constant, Identity] |
Nessuno dei due è migliore | Ambiguo |
List<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ è meglio |
List<byte> viene selezionato |
List<int?> |
List<long> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
Nessuno dei due è migliore | Ambiguo |
List<int?> |
List<ulong> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ è meglio |
List<int?> viene selezionato |
List<short> |
List<long> |
[1, 2, 3] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ è meglio |
List<short> viene selezionato |
IEnumerable<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ è meglio |
IEnumerable<int> viene selezionato |
IEnumerable<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ è meglio |
List<byte> viene selezionato |
int[] |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ è meglio |
int[] viene selezionato |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", "", ""] |
[Identity, Identity, Identity] |
[Implicit Reference, Implicit Reference, Implicit Reference] |
CE₁ᵢ è meglio |
ReadOnlySpan<string> viene selezionato |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", new object()] |
Non applicabile | [Implicit Reference, Identity] |
T₁ non è applicabile |
ReadOnlySpan<object> viene selezionato |
ReadOnlySpan<object> |
Span<string> |
["", ""] |
[Implicit Reference] |
[Identity] |
CE₂ᵢ è meglio |
Span<string> viene selezionato |
ReadOnlySpan<object> |
Span<string> |
[new object()] |
[Identity] |
Non applicabile |
T₁ non è applicabile |
ReadOnlySpan<object> viene selezionato |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{1}"] |
[Interpolated String Handler] |
[Identity] |
CE₁ᵢ è meglio |
ReadOnlySpan<InterpolatedStringHandler> viene selezionato |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{"blah"}"] |
[Interpolated String Handler] |
[Identity] - Ma costante |
CE₂ᵢ è meglio |
ReadOnlySpan<string> viene selezionato |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}"] |
[Identity] |
[Interpolated String] |
CE₂ᵢ è meglio |
ReadOnlySpan<string> viene selezionato |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}", (FormattableString)null] |
Non applicabile | [Interpolated String, Identity] |
T₁ non è applicabile |
ReadOnlySpan<FormattableString> viene selezionato |
HashSet<short> |
Span<long> |
[1, 2] |
[Implicit Constant, Implicit Constant] |
[Implicit Numeric, Implicit Numeric] |
CE₁ᵢ è meglio |
HashSet<short> viene selezionato |
HashSet<long> |
Span<short> |
[1, 2] |
[Implicit Numeric, Implicit Numeric] |
[Implicit Constant, Implicit Constant] |
CE₂ᵢ è meglio |
Span<short> viene selezionato |
Domande aperte
Fino a che punto dovremmo dare priorità a ReadOnlySpan/Span rispetto ad altri tipi?
Come specificato oggi, le seguenti sovraccariche sarebbero ambigue:
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> {}
Quanto lontano vogliamo andare qui? La variante List<T> sembra ragionevole e i sottotipi di List<T> esistono aplenty. Ma la versione HashSet ha una semantica molto diversa, come si è certi che sia effettivamente "peggio" rispetto a ReadOnlySpan in questa API?
C# feature specifications