Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Notatka
Ten artykuł jest specyfikacją funkcji. Specyfikacja służy jako dokument projektowy dla funkcji. Zawiera proponowane zmiany specyfikacji wraz z informacjami wymaganymi podczas projektowania i opracowywania funkcji. Te artykuły są publikowane do momentu sfinalizowania proponowanych zmian specyfikacji i włączenia ich do obecnej specyfikacji ECMA.
Mogą wystąpić pewne rozbieżności między specyfikacją funkcji a ukończoną implementacją. Te różnice są przechwytywane w odpowiednich spotkania projektowego języka (LDM).
Więcej informacji na temat procesu wdrażania specyfikacji funkcji można znaleźć w standardzie języka C# w artykule dotyczącym specyfikacji .
Problem z mistrzem: https://github.com/dotnet/csharplang/issues/8374
Streszczenie
Zaktualizowanie reguł konwersji, aby były bardziej spójne z paramsi lepiej radziły sobie z obecnymi scenariuszami niejednoznaczności. Na przykład ReadOnlySpan<string> a ReadOnlySpan<object> mogą obecnie powodować niejednoznaczności podczas rozwiązywania przeciążenia dla [""].
Szczegółowy projekt
Poniżej przedstawiono lepszą konwersję z reguł wyrażeń. Zastępują one reguły w https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#overload-resolution.
Są to następujące reguły:
Biorąc pod uwagę niejawną konwersję
C₁, która konwertuje z wyrażeniaEna typT₁, oraz niejawną konwersjęC₂, która konwertuje z wyrażeniaEna typT₂,C₁jest lepszą konwersją niżC₂, jeśli zachodzi jeden z następujących warunków:
Ejest wyrażeniem kolekcji , aC₁to lepsza konwersja wyrażenia kolekcji z niżC₂Enie jest wyrażeniem kolekcji i jedno z następujących stwierdzeń jest prawdziwe:
Edokładnie pasuje doT₁iEnie pasuje dokładnie doT₂Edokładnie pasuje albo do żadnego zT₁iT₂, albo do obu, aT₁jest lepszym celem konwersji niżT₂Ejest grupą metod, ...
Dodamy nową definicję dla lepszej konwersji kolekcji z wyrażeniaw następujący sposób:
Podane:
-
Ejest wyrażeniem kolekcji z wyrażeniami elementów[EL₁, EL₂, ..., ELₙ] -
T₁iT₂to typy kolekcji -
E₁jest typem elementuT₁ -
E₂jest typem elementuT₂ -
CE₁ᵢto seria przekształceń odELᵢdoE₁ -
CE₂ᵢto seria przekształceń odELᵢdoE₂
Jeśli istnieje konwersja tożsamości z E₁ na E₂, to konwersje elementów są tak samo dobre jak każda z osobna. W przeciwnym razie konwersje elementów na E₁ są lepsze niż konwersje elementów do E₂, jeśli:
- Dla każdego
ELᵢCE₁ᵢjest przynajmniej tak dobry jakCE₂ᵢ, i - Istnieje co najmniej jedno i, dla którego
CE₁ᵢjest lepsze niżCE₂ᵢ, w przeciwnym razie żaden zestaw konwersji elementów nie jest lepszy od drugiego i nie są one równie dobre.
Porównania konwersji są wykonywane, używając bardziej efektywnej konwersji z wyrażenia, jeśliELᵢnie jest elementem rozkładu. JeśliELᵢjest elementem rozproszenia, stosujemy lepszą konwersję z typu elementu kolekcji rozproszenia doE₁lubE₂, odpowiednio.
- Zarówno
T₁, jak iT₂nie są typów, aT₁jest niejawnie konwertowany naT₂, aT₂nie jest niejawnie konwertowany naT₁, lub -
E₁nie ma konwersji tożsamości doE₂, a konwersje elementów doE₁są lepsze niż konwersje elementów doE₂lub -
E₁ma konwersję tożsamości naE₂, i spełniony jest jeden z następujących warunków:-
T₁jestSystem.ReadOnlySpan<E₁>, aT₂jestSystem.Span<E₂>, lub -
T₁jestSystem.ReadOnlySpan<E₁>lubSystem.Span<E₁>, aT₂jest array_or_array_interface z typem elementuE₂
-
W przeciwnym razie żaden typ kolekcji nie jest lepszy, a wynik jest niejednoznaczny.
Notatka
Te reguły oznaczają, że metody, które uwidaczniają przeciążenia, które przyjmują różne typy elementów i bez konwersji między typami kolekcji, są niejednoznaczne dla pustych wyrażeń kolekcji. Przykład:
public void M(ReadOnlySpan<int> ros) { ... }
public void M(Span<int?> span) { ... }
M([]); // Ambiguous
Scenariuszy:
W języku angielskim typy kolekcji muszą być takie same lub jednoznacznie lepsze (tj. List<T> i List<T> są takie same, List<T> są jednoznacznie lepsze niż IEnumerable<T>i List<T> i HashSet<T> nie można porównać), a konwersje elementów dla lepszego typu kolekcji również muszą być takie same lub lepsze (tj. nie możemy zdecydować między ReadOnlySpan<object> i Span<string> dla [""], użytkownik musi podjąć decyzję). Więcej przykładów:
T₁ |
T₂ |
E |
C₁ konwersje |
C₂ konwersje |
CE₁ᵢ a CE₂ᵢ |
Wynik |
|---|---|---|---|---|---|---|
List<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ jest lepszy |
List<int> zostaje wybrane |
List<int> |
List<byte> |
[(int)1, (byte)2] |
[Identity, Implicit Numeric] |
Nie dotyczy |
T₂ nie ma zastosowania |
List<int> zostaje wybrane |
List<int> |
List<byte> |
[1, (byte)2] |
[Identity, Implicit Numeric] |
[Implicit Constant, Identity] |
Żadna z nich nie jest lepsza | Dwuznaczny |
List<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ jest lepszy |
List<byte> zostaje wybrane |
List<int?> |
List<long> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
Żadna z nich nie jest lepsza | Dwuznaczny |
List<int?> |
List<ulong> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ jest lepszy |
List<int?> zostaje wybrane |
List<short> |
List<long> |
[1, 2, 3] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ jest lepszy |
List<short> zostaje wybrane |
IEnumerable<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ jest lepszy |
IEnumerable<int> zostaje wybrane |
IEnumerable<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ jest lepszy |
List<byte> zostaje wybrane |
int[] |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ jest lepszy |
int[] zostaje wybrane |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", "", ""] |
[Identity, Identity, Identity] |
[Implicit Reference, Implicit Reference, Implicit Reference] |
CE₁ᵢ jest lepszy |
ReadOnlySpan<string> zostaje wybrane |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", new object()] |
Nie dotyczy | [Implicit Reference, Identity] |
T₁ nie ma zastosowania |
ReadOnlySpan<object> zostaje wybrane |
ReadOnlySpan<object> |
Span<string> |
["", ""] |
[Implicit Reference] |
[Identity] |
CE₂ᵢ jest lepszy |
Span<string> zostaje wybrane |
ReadOnlySpan<object> |
Span<string> |
[new object()] |
[Identity] |
Nie dotyczy |
T₁ nie ma zastosowania |
ReadOnlySpan<object> zostaje wybrane |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{1}"] |
[Interpolated String Handler] |
[Identity] |
CE₁ᵢ jest lepszy |
ReadOnlySpan<InterpolatedStringHandler> zostaje wybrane |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{"blah"}"] |
[Interpolated String Handler] |
[Identity] — ale stała wartość |
CE₂ᵢ jest lepszy |
ReadOnlySpan<string> zostaje wybrane |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}"] |
[Identity] |
[Interpolated String] |
CE₂ᵢ jest lepszy |
ReadOnlySpan<string> zostaje wybrane |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}", (FormattableString)null] |
Nie dotyczy | [Interpolated String, Identity] |
T₁ nie ma zastosowania |
ReadOnlySpan<FormattableString> zostaje wybrane |
HashSet<short> |
Span<long> |
[1, 2] |
[Implicit Constant, Implicit Constant] |
[Implicit Numeric, Implicit Numeric] |
CE₁ᵢ jest lepszy |
HashSet<short> zostaje wybrane |
HashSet<long> |
Span<short> |
[1, 2] |
[Implicit Numeric, Implicit Numeric] |
[Implicit Constant, Implicit Constant] |
CE₂ᵢ jest lepszy |
Span<short> zostaje wybrane |
Otwórz pytania
Jak bardzo powinniśmy priorytetyzować ReadOnlySpan/Span nad innymi typami?
Jak określono dzisiaj, następujące przeciążenia byłyby niejednoznaczne:
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> {}
Jak daleko chcemy tu przejść? Wariant List<T> wydaje się rozsądny, a podtypy List<T> istnieją aplenty. Ale czy wersja HashSet ma bardzo różną semantykę, jak bardzo jesteśmy pewni, że jest ona rzeczywiście "gorsza" niż ReadOnlySpan w tym interfejsie API?
C# feature specifications