Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Заметка
Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Она включает предлагаемые изменения спецификации, а также информацию, необходимую во время проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.
Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Эти различия фиксируются в соответствующих заметках собраний по разработке языка (LDM) .
Дополнительные сведения о процессе внедрения спецификаций функций в стандарт языка C# см. в статье о спецификациях .
Проблема чемпиона: https://github.com/dotnet/csharplang/issues/8374
Сводка
Обновляются правила преобразования, которые должны быть более согласованными с params
и более эффективно обрабатывать текущие сценарии неоднозначности. Например, ReadOnlySpan<string>
и ReadOnlySpan<object>
в настоящее время могут вызвать неоднозначность во время разрешения перегрузки для [""]
.
Подробный дизайн
Ниже приведено лучшее преобразование из правил выражений. Эти правила заменяют правила в https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#overload-resolution.
Ниже приведены следующие правила:
Учитывая неявное преобразование
C₁
, которое преобразуется из выраженияE
в типT₁
, и неявное преобразованиеC₂
, которое преобразуется из выраженияE
в типT₂
,C₁
— это лучшее преобразование, чемC₂
, если выполняется одно из следующих условий:
. — это выражение коллекции , а — это лучшее преобразование коллекции из выражения, чем E
не является выражением коллекции , и выполняется одно из следующих условий:
E
точно совпадает сT₁
, аE
не полностью соответствуетT₂
E
точно соответствует либо обоимT₁
иT₂
, либо ни тому, ни другому, аT₁
— это лучшее целевое преобразование по сравнению сT₂
.E
— это группа методов, ...
Мы добавляем новое определение для для более эффективного преобразования коллекции из выраженияследующим образом:
Данный:
-
E
— это выражение коллекции с выражениями элементов[EL₁, EL₂, ..., ELₙ]
-
T₁
иT₂
являются типами коллекций -
E₁
— это тип элементаT₁
-
E₂
— это тип элементаT₂
-
CE₁ᵢ
— это ряд преобразований изELᵢ
вE₁
-
CE₂ᵢ
— это ряд преобразований изELᵢ
вE₂
Если существует тождественное преобразование из E₁
в E₂
, то преобразования элементов так же хороши друг, как и друг. В противном случае преобразования элементов в E₁
лучше, чем преобразования элементов в E₂
, если:
- Для каждого
ELᵢ
CE₁ᵢ
не менее хорош, чемCE₂ᵢ
, и - Существует по крайней мере один i, где
CE₁ᵢ
лучше, чемCE₂ᵢ
. В противном случае ни один набор преобразований элементов не лучше другого, и они также не такие хорошие, как друг друга.
Сравнения преобразований выполняются с помощью более эффективного преобразования из выражения, еслиELᵢ
не является элементом распространения. ЕслиELᵢ
является элементом распространения, мы используем лучшее преобразование из типа элемента коллекции распространения вE₁
илиE₂
соответственно.
C₁
— это лучшее преобразование коллекции из выражения, чем C₂
, если:
- Оба
T₁
иT₂
не типы диапазонов, иT₁
неявно преобразуется вT₂
, иT₂
неявно преобразуется вT₁
или -
E₁
не имеет преобразования идентичности вE₂
, а преобразования элементов вE₁
лучше, чем преобразования элементов вE₂
, или же... -
E₁
имеет тождественное преобразование вE₂
и выполняется одно из следующих условий.-
T₁
этоSystem.ReadOnlySpan<E₁>
, иT₂
этоSystem.Span<E₂>
, или -
T₁
System.ReadOnlySpan<E₁>
илиSystem.Span<E₁>
,T₂
— это array_or_array_interface с типом элементаE₂
-
В противном случае ни один тип коллекции не лучше, и результат неоднозначный.
Заметка
Эти правила означают, что методы, предоставляющие перегрузки, работающие с разными типами элементов без преобразования между типами коллекций, являются неоднозначными для пустых выражений для коллекций. В качестве примера:
public void M(ReadOnlySpan<int> ros) { ... }
public void M(Span<int?> span) { ... }
M([]); // Ambiguous
Сценарии:
На обычном английском языке сами типы коллекций должны быть одинаковыми. или однозначно лучше (т. е. List<T>
и List<T>
совпадают, List<T>
однозначно лучше, чем IEnumerable<T>
, а List<T>
и HashSet<T>
нельзя сравнить), а преобразования элементов для лучшего типа коллекции также должны быть одинаковыми или более лучшими (т. е. мы не можем решить между ReadOnlySpan<object>
и Span<string>
для [""]
, пользователь должен принять это решение). Ниже приведены дополнительные примеры:
T₁ |
T₂ |
E |
C₁ Преобразования |
C₂ Преобразования |
CE₁ᵢ и CE₂ᵢ |
Результат |
---|---|---|---|---|---|---|
List<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ лучше |
List<int> выбран |
List<int> |
List<byte> |
[(int)1, (byte)2] |
[Identity, Implicit Numeric] |
Неприменимо |
T₂ неприменимо |
List<int> выбран |
List<int> |
List<byte> |
[1, (byte)2] |
[Identity, Implicit Numeric] |
[Implicit Constant, Identity] |
Ни один не лучше | Двусмысленный |
List<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ лучше |
List<byte> выбран |
List<int?> |
List<long> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
Ни один не лучше | Двусмысленный |
List<int?> |
List<ulong> |
[1, 2, 3] |
[Implicit Nullable, Implicit Nullable, Implicit Nullable] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ лучше |
List<int?> выбран |
List<short> |
List<long> |
[1, 2, 3] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
[Implicit Numeric, Implicit Numeric, Implicit Numeric] |
CE₁ᵢ лучше |
List<short> выбран |
IEnumerable<int> |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ лучше |
IEnumerable<int> выбран |
IEnumerable<int> |
List<byte> |
[(byte)1, (byte)2] |
[Implicit Numeric, Implicit Numeric] |
[Identity, Identity] |
CE₂ᵢ лучше |
List<byte> выбран |
int[] |
List<byte> |
[1, 2, 3] |
[Identity, Identity, Identity] |
[Implicit Constant, Implicit Constant, Implicit Constant] |
CE₁ᵢ лучше |
int[] выбран |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", "", ""] |
[Identity, Identity, Identity] |
[Implicit Reference, Implicit Reference, Implicit Reference] |
CE₁ᵢ лучше |
ReadOnlySpan<string> выбран |
ReadOnlySpan<string> |
ReadOnlySpan<object> |
["", new object()] |
Неприменимо | [Implicit Reference, Identity] |
T₁ неприменимо |
ReadOnlySpan<object> выбран |
ReadOnlySpan<object> |
Span<string> |
["", ""] |
[Implicit Reference] |
[Identity] |
CE₂ᵢ лучше |
Span<string> выбран |
ReadOnlySpan<object> |
Span<string> |
[new object()] |
[Identity] |
Неприменимо |
T₁ неприменимо |
ReadOnlySpan<object> выбран |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{1}"] |
[Interpolated String Handler] |
[Identity] |
CE₁ᵢ лучше |
ReadOnlySpan<InterpolatedStringHandler> выбран |
ReadOnlySpan<InterpolatedStringHandler> |
ReadOnlySpan<string> |
[$"{"blah"}"] |
[Interpolated String Handler] |
[Identity] - Но константа |
CE₂ᵢ лучше |
ReadOnlySpan<string> выбран |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}"] |
[Identity] |
[Interpolated String] |
CE₂ᵢ лучше |
ReadOnlySpan<string> выбран |
ReadOnlySpan<string> |
ReadOnlySpan<FormattableString> |
[$"{1}", (FormattableString)null] |
Неприменимо | [Interpolated String, Identity] |
T₁ неприменимо |
ReadOnlySpan<FormattableString> выбран |
HashSet<short> |
Span<long> |
[1, 2] |
[Implicit Constant, Implicit Constant] |
[Implicit Numeric, Implicit Numeric] |
CE₁ᵢ лучше |
HashSet<short> выбран |
HashSet<long> |
Span<short> |
[1, 2] |
[Implicit Numeric, Implicit Numeric] |
[Implicit Constant, Implicit Constant] |
CE₂ᵢ лучше |
Span<short> выбран |
Открытые вопросы
В какой степени мы должны расставлять приоритеты ReadOnlySpan
/Span
над другими типами?
Как указано сегодня, следующие перегрузки будут неоднозначными:
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> {}
Насколько далеко мы хотим пойти сюда? Вариант List<T>
кажется разумным, и подтипов List<T>
существует много. Но версия HashSet
имеет очень другую семантику, насколько мы уверены, что это на самом деле "хуже", чем ReadOnlySpan
в этом API?
C# feature specifications