10 преобразований
10.1 Общие
Преобразование приводит к преобразованию выражения в определенный тип или его обработке; в предыдущем случае преобразование может привести к изменению представления. Преобразования могут быть неявными или явными, и это определяет, требуется ли явное приведение.
Пример. Например, преобразование типа в тип неявно, поэтому выражения типа
int
long
int
могут неявно рассматриваться как тип.long
Противоположное преобразование, от типа к типуlong
int
, является явным и поэтому требуется явное приведение.int a = 123; long b = a; // implicit conversion from int to long int c = (int) b; // explicit conversion from long to int
пример конца
Некоторые преобразования определяются языком. Программы также могут определять собственные преобразования (§10.5).
Некоторые преобразования языка определяются из выражений в типы, другие из типов в типы. Преобразование типа применяется ко всем выражениям, имеющим этот тип.
Пример:
enum Color { Red, Blue, Green } // The expression 0 converts implicitly to enum types Color c0 = 0; // Other int expressions need explicit conversion Color c1 = (Color)1; // Conversion from null expression (no type) to string string x = null; // Conversion from lambda expression to delegate type Func<int, int> square = x => x * x;
пример конца
10.2 Неявные преобразования
10.2.1 Общие
Следующие преобразования классифицируются как неявные преобразования:
- Преобразования удостоверений (§10.2.2)
- Неявные числовые преобразования (§10.2.3)
- Неявные преобразования перечисления (§10.2.4)
- Неявные интерполированные преобразования строк (§10.2.5)
- Неявные преобразования ссылок (§10.2.8)
- Преобразования бокса (§10.2.9)
- Неявные динамические преобразования (§10.2.10)
- Преобразования параметров неявного типа (§10.2.12)
- Преобразования неявных констант (§10.2.11)
- Определяемые пользователем (включая отмененные) неявные преобразования (§10.2.14)
- Анонимные преобразования функций (§10.2.15)
- Преобразования групп методов (§10.2.15)
- Преобразования литералов NULL (§10.2.7)
- Неявные преобразования, допускающие значение NULL (§10.2.6)
- Неявные преобразования кортежей (§10.2.13)
- Преобразования литералов по умолчанию (§10.2.16)
- Неявные преобразования вызовов (§10.2.17)
Неявные преобразования могут возникать в различных ситуациях, включая вызовы члена функции (§12.6.6), выражения приведения (§12.9.7) и назначения (§12.21).
Предварительно определенные неявные преобразования всегда успешно и никогда не вызывают возникновения исключений.
Примечание. Правильно разработанные пользователем неявные преобразования должны также демонстрировать эти характеристики. конечная заметка
В целях преобразования типы object
и dynamic
являются преобразованными удостоверениями (§10.2.2).
Однако динамические преобразования (§10.2.10dynamic
§8.2.4).
Преобразование удостоверений 10.2.2
Преобразование удостоверений преобразуется из любого типа в тот же тип или тип, эквивалентный во время выполнения. Одна из причин, по которой это преобразование существует, заключается в том, что тип T
или выражение типа T
может быть преобразовано в T
себя. Существуют следующие преобразования удостоверений:
- Между
T
иT
, для любого типаT
. -
T
МеждуT?
и для любого ссылочного типаT
. - Между
object
иdynamic
. - Между всеми типами кортежей с одинаковым arity и соответствующим созданным
ValueTuple<...>
типом, когда преобразование удостоверений существует между каждой парой соответствующих типов элементов. - Между типами, созданными из одного универсального типа, где существует преобразование удостоверений между каждым соответствующим аргументом типа.
Пример. Ниже показан рекурсивный характер третьего правила:
(int a , string b) t1 = (1, "two"); (int c, string d) t2 = (3, "four"); // Identity conversions exist between // the types of t1, t2, and t3. var t3 = (5, "six"); t3 = t2; t2 = t1; var t4 = (t1, 7); var t5 = (t2, 8); // Identity conversions exist between // the types of t4, t5, and t6. var t6 =((8, "eight"), 9); t6 = t5; t5 = t4;
Типы кортежей
t1
иt2
t3
все имеют два элемента:int
за которым следуетstring
. Типы элементов кортежа могут сами по себе кортежами, как и вt4
,t5
иt6
. Преобразование удостоверений существует между каждой парой соответствующих типов элементов, включая вложенные кортежи, поэтому преобразование удостоверений существует между типами кортежейt4
иt5
t6
.пример конца
Все преобразования удостоверений являются симметричными. Если преобразование удостоверений существует из T₁
T₂
, преобразование удостоверений существует из T₂
T₁
. Два типа — это преобразование удостоверений, когда преобразование удостоверений существует между двумя типами.
В большинстве случаев преобразование удостоверений не влияет на среду выполнения. Однако так как операции с плавающей запятой могут выполняться с более высокой точностью, чем предписано их типом (§8.3.7), назначение их результатов может привести к потере точности, и явные приведения гарантированно снижают точность до того, что предписано типом (§12.9.7).
10.2.3 Неявные числовые преобразования
Неявные числовые преобразования:
- От
sbyte
, до,short
,int
long
,float
илиdouble
.decimal
-
byte
short
ushort
int
uint
long
ulong
float
Отdouble
, или .decimal
- От
short
, доint
,long
,float
double
илиdecimal
. - От , , ,
ushort
int
uint
long
или .ulong
float
double
decimal
- От
int
, доlong
,float
double
илиdecimal
. - От
uint
, доlong
,ulong
,float
double
илиdecimal
. -
long
Отfloat
до ,double
илиdecimal
. -
ulong
Отfloat
до ,double
илиdecimal
. -
char
ushort
int
uint
long
ulong
float
Отdouble
, или .decimal
- С
float
наdouble
.
Преобразования из , int
uint
или long
ulong
из float
или из long
или ulong
double
могут привести к потере точности, но никогда не приведет к потере величины. Другие неявные числовые преобразования никогда не теряют никаких сведений.
Не существует предопределенных неявных преобразований в char
тип, поэтому значения других целочисленных типов не преобразуются char
в тип автоматически.
Преобразования неявного перечисления 10.2.4
Преобразование неявного перечисления позволяет constant_expression (§12.23) с любым целым типом и нулевым значением, преобразованным в любой enum_type и в любой nullable_value_type, базовый тип которого является enum_type. В последнем случае преобразование вычисляется путем преобразования в базовый enum_type и упаковки результата (§8.3.12).
10.2.5 Неявные интерполированные преобразования строк
Неявное интерполированное преобразование строк позволяет преобразовать interpolated_string_expression (§12.8.3) в System.IFormattable
или System.FormattableString
(реализующий System.IFormattable
).
При применении этого преобразования строковое значение не состоит из интерполированной строки. Вместо этого создается экземпляр System.FormattableString
, как описано далее в разделе 12.8.3.
10.2.6 Неявные преобразования, допускающие значение NULL
Неявные преобразования, допускающие значение NULL, — это преобразования, допускающие значение NULL (§10.6.1), производные от неявных предопределенных преобразований.
Преобразования литералов NULL 10.2.7
Неявное преобразование существует из литерала в null
любой ссылочный тип или тип значений, допускающий значение NULL. Это преобразование создает пустую ссылку, если целевой тип является ссылочным типом или значением NULL (§8.3.12) заданного типа значения NULL.
10.2.8 Неявные преобразования ссылок
Неявные преобразования ссылок:
- От любого reference_type до
object
иdynamic
. - От любого class_type до любой
T
предоставляетсяS
производный от .T
- От любого class_type до любой
T
, предоставленнойS
T
реализации. - От любого interface_type до любой
T
предоставляетсяS
производный от .T
-
Sᵢ
T
с типомTᵢ
элемента, если все следующие значения имеют значение true:-
S
иT
отличаются только в типе элемента. Другими словами,S
иT
имеют то же количество измерений. - Неявное преобразование ссылок существует из
Sᵢ
Tᵢ
.
-
- Из одномерного типа
S[]
System.Collections.Generic.IList<T>
System.Collections.Generic.IReadOnlyList<T>
массива в , а также их базовые интерфейсы, при условии, что существует неявное удостоверение или преобразование ссылок из .S
T
- Из любого array_type
System.Array
и интерфейсов, которые он реализует. - Из любого delegate_type
System.Delegate
в интерфейсы, которые он реализует. - От литерала NULL (§6.4.5.7) до любого ссылочного типа.
- От любого
T
, если он имеет неявное удостоверение или преобразование ссылок на reference_typeT₀
иT₀
имеет преобразованиеT
удостоверений в . - Из любого
T
. - Неявные преобразования, включающие параметры типа, известные как ссылочные типы. Дополнительные сведения о неявных преобразованиях, связанных с параметрами типа, см. в статье 10.2.12 .
Неявные преобразования ссылок — это преобразования между reference_types, которые могут быть проверены всегда успешно, и поэтому не требуют проверок во время выполнения.
Преобразования ссылок, неявные или явные, никогда не изменяют референциальное удостоверение преобразованного объекта.
Примечание. Другими словами, в то время как преобразование ссылок может изменить тип ссылки, он никогда не изменяет тип или значение объекта, на который ссылается ссылка. конечная заметка
Преобразования бокса 10.2.9
Преобразование бокса позволяет неявно преобразовать value_type в reference_type. Существуют следующие преобразования бокса:
- От любого value_type к типу
object
. - От любого value_type к типу
System.ValueType
. - От любого enum_type к типу
System.Enum
. - От любого non_nullable_value_type до любого interface_type, реализованного non_nullable_value_type.
- От любого non_nullable_value_type до любого interface_type таким образом, что преобразование бокса из
I
в другойI₀
иI₀
преобразованиеI
удостоверения в . - От любого non_nullable_value_type к любой interface_type таким образом, что преобразование бокса из
I
в другой interface_typeI₀
, иI₀
является вариативным (§18.2.3.3) в .I
- От любого nullable_value_type до любого reference_type, где имеется преобразование бокса из базового типа nullable_value_type в reference_type.
- Из параметра типа, который не является ссылочным типом любого типа, таким образом, что преобразование разрешено в §10.2.12.
Поле значения типа, отличного от null-value, состоит из выделения экземпляра объекта и копирования значения в этот экземпляр.
Поле значения nullable_value_type создает ссылку null, если это значение NULL (HasValue
равно false), или результат распакуивания и поля базового значения в противном случае.
Примечание. Процесс бокса может представляться с точки зрения существования класса бокса для каждого типа значения. Например, рассмотрим реализацию
struct S
интерфейсаI
с именемS_Boxing
класса бокса.interface I { void M(); } struct S : I { public void M() { ... } } sealed class S_Boxing : I { S value; public S_Boxing(S value) { this.value = value; } public void M() { value.M(); } }
Поле значения
v
типаS
теперь состоит из выполнения выраженияnew S_Boxing(v)
и возврата результирующего экземпляра в качестве значения целевого типа преобразования. Таким образом, инструкцииS s = new S(); object box = s;
можно рассматривать как похожие на:
S s = new S(); object box = new S_Boxing(s);
Описанный выше тип бокса не существует. Вместо этого прямоугольное значение типа
S
имеет типS
среды выполнения, а проверка типа среды выполнения с помощьюis
оператора со типом значения проверяет, является ли левый операнд версией правого операнда. Например,int i = 123; object box = i; if (box is int) { Console.Write("Box contains an int"); }
выводит следующее:
Box contains an int
Преобразование бокса подразумевает создание копии прямоугольного значения. Это отличается от преобразования
object
. Например, нижеstruct Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { void M() { Point p = new Point(10, 10); object box = p; p.x = 20; Console.Write(((Point)box).x); } }
выводит значение 10 на консоли, так как неявная операция бокса, возникающая в назначении,
p
box
приводит к копированию значенияp
.Point
Вместоclass
этого значение 20 будет выходным, посколькуp
иbox
будет ссылаться на тот же экземпляр.Аналогия класса бокса не должна использоваться как более полезное средство для изображения того, как бокс работает концептуально. Существует множество тонких различий между поведением, описанным в этой спецификации, и поведением, которое приведет к реализации бокса точно таким образом.
конечная заметка
10.2.10 Неявные динамические преобразования
Неявное динамическое преобразование существует из выражения типа dynamic к любому типу T
. Преобразование динамически привязано к §12.3.3, что означает, что неявное преобразование будет искаться во время выполнения из типа времени выполнения выражения T
в . Если преобразование не найдено, создается исключение во время выполнения.
Это неявное преобразование, казалось бы, нарушает совет в начале §10.2 , что неявное преобразование никогда не должно вызывать исключение. Однако это не само преобразование, а поиск преобразования, вызывающего исключение. Риск исключений во время выполнения связан с использованием динамической привязки. Если динамическая привязка преобразования не требуется, выражение можно сначала преобразовать object
в , а затем в нужный тип.
Пример: ниже показаны неявные динамические преобразования:
object o = "object"; dynamic d = "dynamic"; string s1 = o; // Fails at compile-time – no conversion exists string s2 = d; // Compiles and succeeds at run-time int i = d; // Compiles but fails at run-time – no conversion exists
Назначения и
s2
i
оба используют неявные динамические преобразования, где привязка операций приостановлена до времени выполнения. Во время выполнения неявные преобразования ищутся из типаd
времени выполнения (string
) в целевой тип. Преобразование найдено,string
но не вint
.пример конца
Преобразования неявных константных выражений 10.2.11
Неявное преобразование константного выражения позволяет выполнить следующие преобразования:
- Constant_expression (§12.23) типа можно преобразовать в тип
int
sbyte
, ,byte
,short
ushort
uint
илиulong
, если значение constant_expression находится в диапазоне целевого типа. - Constant_expression типа можно преобразовать в тип
long
, если значениеulong
не является отрицательным.
10.2.12 Неявные преобразования с параметрами типа
T
, который, как известно, является ссылочным типом (§15.2.5), существуют следующие неявные преобразования ссылок (§10.2.8):
- От
T
до эффективного базового классаC
, отT
любого базового класса до любого базового классаC
и отT
любого интерфейса, реализованногоC
. - От
T
interface_typeT
наборе интерфейсов и отT
любого базового интерфейсаI
. - От
T
параметра типа,U
указанного в зависимости отT
(U
).Примечание. Поскольку
T
в пределах областиT
действия тип времениU
выполнения всегда будет ссылочным типом, даже еслиU
он не является ссылочным типом во время компиляции. конечная заметка - От литерала NULL (§6.4.5.7) до T.
T
, который не является ссылочным типом §15.2.5, в время компиляции рассматриваются следующие преобразования, связанные T
с боксом (§10.2.9). Во время выполнения, если T
это тип значения, преобразование выполняется как преобразование бокса. Во время выполнения, если T
это ссылочный тип, преобразование выполняется как неявное преобразование ссылок или преобразование удостоверений.
- От
T
до эффективного базового классаC
, отT
любого базового класса до любого базового классаC
и отT
любого интерфейса, реализованногоC
.Примечание.
C
Будет одним из типовSystem.Object
илиSystem.ValueType
(вSystem.Enum
противном случаеT
может быть ссылочным типом). конечная заметка - От
T
interface_typeT
наборе интерфейсов и отT
любого базового интерфейсаI
.
Для type_parameterT
, не известного как ссылочный тип, существует неявное преобразование из T
указанного U
параметра типа в зависимости от T
указанного параметраU
. Во время выполнения, если T
является типом значения и U
является ссылочным типом, преобразование выполняется как преобразование бокса. Во время выполнения, если оба T
U
типа и являются типами значений, то T
и U
обязательно являются одинаковыми и не выполняются преобразования. Во время выполнения, если T
это ссылочный тип, то U
также является ссылочным типом, а преобразование выполняется как неявное преобразование ссылок или преобразование удостоверений (§15.2.5).
Для заданного параметра T
типа существуют следующие дальнейшие неявные преобразования:
- От
T
типа ссылки, если он имеет неявное преобразование в ссылочный типS
S₀
иS₀
имеет преобразованиеS
удостоверений в . Во время выполнения преобразование выполняется так же, как и преобразованиеS₀
в . - От
T
типаI
интерфейса, если он имеет неявное преобразование в типI₀
интерфейса, иI₀
имеет дисперсию преобразование вI
(§18.2.3.3). Во время выполнения, еслиT
это тип значения, преобразование выполняется как преобразование бокса. В противном случае преобразование выполняется как неявное преобразование ссылок или преобразование удостоверений.
Во всех случаях правила гарантируют, что преобразование выполняется как преобразование в бокс, если и только если во время выполнения преобразование выполняется из типа значения в ссылочный тип.
10.2.13 Неявные преобразования кортежей
Неявное преобразование существует из выражения E
кортежа в тип T
кортежа, если E
имеет ту же arity, что T
и неявное преобразование существует из каждого элемента в E
соответствующий тип элемента.T
Преобразование выполняется путем создания экземпляра соответствующего T
System.ValueTuple<...>
типа и инициализации каждого из его полей в порядке слева направо путем вычисления соответствующего выражения E
элемента кортежа, преобразования его в соответствующий тип T
элемента с использованием неявного преобразования и инициализации поля с результатом.
Если имя элемента в выражении кортежа не соответствует соответствующему имени элемента в типе кортежа, будет выдано предупреждение.
Пример:
(int, string) t1 = (1, "One"); (byte, string) t2 = (2, null); (int, string) t3 = (null, null); // Error: No conversion (int i, string s) t4 = (i: 4, "Four"); (int i, string) t5 = (x: 5, s: "Five"); // Warning: Names are ignored
Объявления
t1
,t2
t4
иt5
все допустимые, так как неявные преобразования существуют из выражений элементов в соответствующие типы элементов. Объявлениеt3
недопустимо, так как преобразование не выполняется.null
int
Объявлениеt5
вызывает предупреждение, так как имена элементов в выражении кортежа отличаются от имен элементов в типе кортежа.пример конца
10.2.14 Определяемые пользователем неявные преобразования
Определяемое пользователем неявное преобразование состоит из необязательного стандартного неявного преобразования, за которым следует выполнение определяемого пользователем неявного оператора преобразования, за которым следует другое необязательное стандартное неявное преобразование. Точные правила для оценки неявных преобразований, определенных пользователем, описаны в разделе §10.5.4.
10.2.15 Анонимные преобразования функций и преобразования групп методов
Анонимные функции и группы методов не имеют типов и сами по себе, но они могут быть неявно преобразованы в типы делегатов. Кроме того, некоторые лямбда-выражения могут быть неявно преобразованы в типы дерева выражений. Анонимные преобразования функций подробно описаны в разделе §10.7 и преобразования групп методов в §10.8.
Преобразования литералов по умолчанию 10.2.16
Неявное преобразование существует из default_literal (§12.8.21) в любой тип. Это преобразование создает значение по умолчанию (§9.3) выводимого типа.
10.2.17 Неявные преобразования вызовов
Хотя выражения создания не имеют типа, они могут быть неявно преобразованы в любой тип.
10.3 Явные преобразования
10.3.1 Общие
Следующие преобразования классифицируются как явные преобразования.
- Все неявные преобразования (§10.2)
- Явные числовые преобразования (§10.3.2)
- Явные преобразования перечисления (§10.3.3)
- Явные преобразования, допускающие значение NULL (§10.3.4)
- Явные преобразования кортежей (§10.3.6)
- Явные преобразования ссылок (§10.3.5)
- Явные преобразования интерфейса
- Распаковка преобразований (§10.3.7)
- Явные преобразования параметров типа (§10.3.8)
- Определяемые пользователем явные преобразования (§10.3.9)
Явные преобразования могут возникать в выражениях приведения (§12.9.7).
Набор явных преобразований включает все неявные преобразования.
Примечание. Например, это позволяет явно использовать приведение при наличии неявного преобразования удостоверений, чтобы принудительно выбрать перегрузку определенного метода. конечная заметка
Явные преобразования, которые не являются неявными преобразованиями, являются преобразованиями, которые не могут быть проверены всегда для успешного выполнения, преобразования, которые, возможно, теряют информацию, и преобразования между доменами типов достаточно отличаются, чтобы заслуживают явной нотации.
10.3.2 Явные числовые преобразования
Явные числовые преобразования — это преобразования из numeric_type в другую numeric_type, для которой неявное числовое преобразование (§10.2.3) еще не существует:
- От
sbyte
, доbyte
,ushort
,uint
ulong
илиchar
. -
byte
Отsbyte
илиchar
. - От
short
, до,sbyte
,byte
ushort
,uint
илиulong
.char
- От
ushort
, доsbyte
,byte
short
илиchar
. - От , , ,
int
sbyte
byte
short
или .ushort
uint
ulong
char
- От
uint
, до,sbyte
,byte
short
,ushort
илиint
.char
-
long
sbyte
byte
short
ushort
int
uint
Отulong
, или .char
-
ulong
sbyte
byte
short
ushort
int
uint
Отlong
, или .char
-
char
Отsbyte
до ,byte
илиshort
. -
float
sbyte
byte
short
ushort
int
uint
long
ulong
Отchar
, или .decimal
-
double
sbyte
byte
short
ushort
int
uint
long
ulong
char
Отfloat
, или .decimal
-
decimal
sbyte
byte
short
ushort
int
uint
long
ulong
char
Отfloat
, или .double
Так как явные преобразования включают все неявные и явные числовые преобразования, всегда можно преобразовать из любого numeric_type в любые другие numeric_type с помощью выражения приведения (§12.9.7).
Явные числовые преобразования, возможно, теряют информацию или могут привести к возникновению исключений. Явное числовое преобразование обрабатывается следующим образом:
- Для преобразования целочисленного типа в другой целочисленный тип обработка зависит от контекста проверки переполнения (§12.8.20), в котором происходит преобразование:
-
checked
В контексте преобразование завершается успешно, если значение исходного операнда находится в диапазоне целевого типа, но вызываетSystem.OverflowException
исключение, если значение исходного операнда выходит за пределы диапазона целевого типа. - В контексте
unchecked
преобразование всегда выполняется успешно и выполняется следующим образом.- Если исходный тип больше целевого типа, то исходное значение усечено путем отмены его "дополнительных" наиболее значимых битов. Результат затем обрабатывается как значение целевого типа.
- Если исходный тип совпадает с типом назначения, то исходное значение рассматривается как значение целевого типа.
-
- Для преобразования из
decimal
целочисленного типа исходное значение округляется до нуля до ближайшего целочисленного значения, и это целочисленное значение становится результатом преобразования. Если результирующее целочисленное значение выходит за пределы диапазона целевого типа,System.OverflowException
создается исключение. - Для преобразования из
float
целочисленногоdouble
типа обработка зависит от контекста проверки переполнения (§12.8.20), в котором выполняется преобразование:- В проверенном контексте преобразование выполняется следующим образом:
- Если значение операнда равно NaN или бесконечно,
System.OverflowException
создается исключение. - В противном случае исходный операнд округляется до нуля до ближайшего целочисленного значения. Если это целочисленное значение находится в диапазоне целевого типа, это значение является результатом преобразования.
- В противном случае возникает исключение
System.OverflowException
.
- Если значение операнда равно NaN или бесконечно,
- В незаверяемом контексте преобразование всегда выполняется успешно и выполняется следующим образом.
- Если значение операнда равно NaN или бесконечно, результат преобразования является неопределенным значением целевого типа.
- В противном случае исходный операнд округляется до нуля до ближайшего целочисленного значения. Если это целочисленное значение находится в диапазоне целевого типа, это значение является результатом преобразования.
- В противном случае результат преобразования является неопределенным значением целевого типа.
- В проверенном контексте преобразование выполняется следующим образом:
- Для преобразования из
double
float
в ,double
значение округляется до ближайшегоfloat
значения.double
Если значение слишком мало для представления в видеfloat
, результат становится ноль с тем же знаком, что и значение. Если величинаdouble
значения слишком велика для представления в видеfloat
, результат становится бесконечностью с тем же знаком, что и значение.double
Если значение равно NaN, результат также является NaN. - Для преобразования из
float
илиdouble
decimal
в , исходное значение преобразуется вdecimal
представление и округляется до ближайшего числа при необходимости (§8.3.8).- Если исходное значение слишком мало для представления в виде
decimal
, результат становится нулевым, сохраняя знак исходного значения, еслиdecimal
поддерживает подписанные нулевые значения. - Если величина исходного значения слишком велика, чтобы представить как
decimal
бесконечность, результатом является бесконечность, сохраняющая знак исходного значения, если десятичное представление поддерживает бесконечность; в противном случае создается System.OverflowException. - Если исходное значение равно NaN, результатом является NaN, если десятичное представление поддерживает NaNs; в противном случае возникает исключение System.OverflowException.
- Если исходное значение слишком мало для представления в виде
- Для преобразования из
decimal
float
илиdouble
,decimal
значение округляется до ближайшегоdouble
илиfloat
значения. Если величина исходного значения слишком велика, чтобы представить в целевом типе или значение бесконечности, результатом является бесконечность, сохраняющая знак исходного значения. Если исходное значение равно NaN, результатом является NaN. Хотя это преобразование может потерять точность, это никогда не приводит к возникновению исключения.
Примечание. Тип
decimal
не требуется для поддержки определенных значений или значенийfloat
NaN, но может сделать это; его диапазон может быть меньше диапазона иdouble
, но не гарантируется. Дляdecimal
представлений без определенных значений или значений NaN и с диапазоном меньше, чемfloat
результат преобразования изdecimal
любогоfloat
илиdouble
никогда не будет бесконечностью или NaN. конечная заметка
10.3.3 Явные преобразования перечисления
Явные преобразования перечисления:
- От
sbyte
,byte
,short
ushort
int
uint
long
ulong
char
float
double
илиdecimal
любой enum_type. - От любого
uint
long
ulong
char
float
double
decimal
до , или . - От любого enum_type до любого другого enum_type.
Явное преобразование перечисления между двумя типами обрабатывается путем обработки всех участвующих enum_type в качестве базового типа этого enum_type, а затем выполнения неявного или явного числового преобразования между результирующей типы.
Пример. Учитывая
E
с и базовым типомint
, преобразование из нее обрабатывается как явное числовое преобразованиеE
byte
(§10.3.2)int
byte
в , а преобразование изbyte
E
неявного числового преобразования (§10.2.3) в .byte
int
пример конца
10.3.4 Явные преобразования, допускающие значение NULL
Явные преобразования, допускающие значение NULL, — это преобразования, допускающие значение NULL (§10.6.1), производные от явных и неявных предопределенных преобразований.
10.3.5 Явные преобразования ссылок
Явные преобразования ссылок:
- Из объекта в любой другой reference_type.
- От любого class_type до любого
T
предоставляетсяS
базовый класс.T
- От любого class_type до любой
S
T
предоставляетсяS
не запечатанный и предоставленныйS
не реализуется.T
- От любого interface_type до любого
S
T
предоставляетсяT
не запечатанный или предоставленныйT
S
реализации. - От любого interface_type до любой
T
предоставляетсяS
не является производным от.T
-
Sᵢ
T
с типомTᵢ
элемента, если все следующие значения имеют значение true:-
S
иT
отличаются только в типе элемента. Другими словами,S
иT
имеют то же количество измерений. - Явное преобразование ссылок существует из
Sᵢ
Tᵢ
.
-
- От
System.Array
и интерфейсов, которые он реализует, в любой array_type. - Из одномерной
System.Collections.Generic.IReadOnlyList<T>
в , а также его базовые интерфейсы, при условии, что имеется преобразование удостоверений или явное преобразование ссылок в .S
T
- От
System.Collections.Generic.IList<S>
,System.Collections.Generic.IReadOnlyList<S>
и их базовых интерфейсов к одномерным типуT[]
массива, при условии, что имеется преобразование удостоверений или явное преобразование ссылок изS
T. - От
System.Delegate
и интерфейсов, которые он реализует в любой delegate_type. - Из ссылочного типа в ссылочный тип
S
, если он имеет явное преобразование ссылок изT
ссылочного типа в ссылочный типS
иT₀
имеется преобразование удостоверений вT₀
T₀
.T
- От ссылочного типа
S
к интерфейсу или типуT
делегата, если он имеет явное преобразование ссылок изS
интерфейса или делегатаT₀
, и либоT₀
является вариативным или преобразуемымT
T
T₀
в §18.2.3.3. - От
D<S₁...Sᵥ>
места, гдеD<T₁...Tᵥ>
является универсальным типом делегата,D<X₁...Xᵥ>
несовместим с или идентичнымD<S₁...Sᵥ>
и для каждого параметраD<T₁...Tᵥ>
типа из следующих удержанийXᵢ
:D
- Если
Xᵢ
он инвариантный, тоSᵢ
он идентиченTᵢ
. - Если
Xᵢ
это ковариант, то происходит преобразование удостоверений, неявное преобразование ссылок или явное преобразование ссылок изSᵢ
Tᵢ
. - Если
Xᵢ
это контравариант, тоSᵢ
иTᵢ
оба ссылочных типа идентичны или оба ссылочных типа.
- Если
- Явные преобразования, включающие параметры типа, которые, как известно, являются ссылочными типами. Дополнительные сведения о явных преобразованиях, связанных с параметрами типа, см. в разделе "10.3.8".
Явные преобразования ссылок — это преобразования между reference_types, которые требуют проверки во время выполнения, чтобы убедиться, что они верны.
Для успешного выполнения явного преобразования ссылок значение исходного операнда должно быть, или тип объекта, на который ссылается исходный операнд, должен быть null
типом, который можно преобразовать в тип назначения с помощью неявного преобразования ссылок (§10.2.8). Если явное преобразование ссылок завершается ошибкой, System.InvalidCastException
создается исключение.
Примечание. Преобразования ссылок, неявные или явные, никогда не изменяйте значение самой ссылки (§8.2.1), только его тип. Кроме того, он не изменяет тип или значение объекта, на который ссылается объект. конечная заметка
10.3.6 Явные преобразования кортежей
Явное преобразование существует из выражения E
кортежа в тип T
кортежа, если E
имеет то же arity, что T
и неявное или явное преобразование существует из каждого элемента в E
соответствующий тип элемента.T
Преобразование выполняется путем создания экземпляра соответствующего T
типа и инициализации каждого из его полей в порядке слева направо путем вычисления соответствующего выражения System.ValueTuple<...>
элемента кортежа, преобразования его в соответствующий тип E
элемента с использованием явно найденного преобразования и инициализации поля с результатом.T
10.3.7 Распаковка преобразований
Преобразование распаковки позволяет явно преобразовать reference_type в value_type. Существуют следующие преобразования распаковки:
- От типа
object
до любого value_type. - От типа
System.ValueType
до любого value_type. - От типа
System.Enum
до любого enum_type. - От любого interface_type до любого non_nullable_value_type, реализующего interface_type.
- От любого interface_type до любого
I
, где происходит распаковка преобразования из interface_type в типI₀
и преобразование удостоверений в .I
I₀
- От любого interface_type до любой
I
, в которой происходит распаковка преобразования изI₀
и либоI₀
variance_convertible вI
илиI
является вариативнымI₀
преобразованием (§18.2.3.3.3). - От любого reference_type до любого nullable_value_type, где происходит распаковка преобразования из reference_type в базовый non_nullable_value_typenullable_value_type.
- Из параметра типа, который не является типом значения для любого типа, таким образом, что преобразование разрешено в §10.3.8.
Операция распаковки в non_nullable_value_type состоит из первой проверки того, что экземпляр объекта является полем для заданного non_nullable_value_type, а затем копирует значение из экземпляра.
Распаковка в nullable_value_type создает значение NULL nullable_value_type, если исходный операнд имеет null
значение, или результат распаковки экземпляра объекта в базовый тип nullable_value_type в противном случае.
Примечание. Ссылаясь на мнимый класс бокса, описанный в §10.2.9, преобразование поля объекта в value_type
S
состоит из выполнения выражения((S_Boxing)box).value
. Таким образом, инструкцииobject box = new S(); S s = (S)box;
концептуально соответствует
object box = new S_Boxing(new S()); S s = ((S_Boxing)box).value;
конечная заметка
Для отмены преобразования в заданный non_nullable_value_type для успешного выполнения значение исходного операнда должно быть ссылкой на поле этого non_nullable_value_type. Если исходный null
операнд вызываетсяSystem.NullReferenceException
. Если исходный операнд является ссылкой на несовместимый объект, System.InvalidCastException
создается исключение.
Для отмены преобразования в заданный nullable_value_type для успешного выполнения значение исходного операнда должно иметь значение NULL или ссылку на поле базового non_nullable_value_type nullable_value_type. Если исходный операнд является ссылкой на несовместимый объект, System.InvalidCastException
создается исключение.
10.3.8 Явные преобразования с параметрами типа
T
, который, как известно, является ссылочным типом (§15.2.5), существуют следующие явные преобразования ссылок (§10.3.5):
- От эффективного
C
базового классаT
кT
любому базовому классу и от любого базового классаC
T
до . - От любого interface_type до
T
. - От
T
любого interface_typeI
, предоставленного, еще не выполняется неявное преобразование ссылок вT
I
. -
U
,T
котороеT
зависит отU
(§15.2.5).Примечание. Поскольку
T
в пределах областиT
действия тип времени выполнения всегда будет ссылочным типом, даже еслиU
он не является ссылочным типом во время компиляции. конечная заметка
T
, который не является ссылочным типом (§15.2.5), следующие преобразования, связанные T
с компиляцией, считаются распаковкой преобразований (§10.3.7). Во время выполнения, если T
это тип значения, преобразование выполняется в виде преобразования распаковки. Во время выполнения, если T
это ссылочный тип, преобразование выполняется как явное преобразование ссылок или преобразование удостоверений.
- От эффективного
C
базового классаT
кT
любому базовому классу и от любого базового классаC
T
до .Примечание. C будет одним из типов
System.Object
,System.ValueType
или (вSystem.Enum
противном случаеT
может быть ссылочным типом). конечная заметка - От любого interface_type до
T
.
T
, который не известен как ссылочный тип (§15.2.5), существуют следующие явные преобразования:
- От
T
любого interface_typeI
, предоставленного, еще не выполняется неявное преобразование изT
I
. Это преобразование состоит из неявного преобразования бокса (§10.2.9),T
object
за которым следует явное преобразование ссылок вobject
I
. При выполнении, еслиT
это тип значения, преобразование выполняется как преобразование в поле, за которым следует явное преобразование ссылок. Во время выполнения, еслиT
это ссылочный тип, преобразование выполняется как явное преобразование ссылок. - От параметра
U
типа, указанногоT
вT
зависимости отU
(§15.2.5). Во время выполнения, еслиT
это тип значения иU
является ссылочным типом, преобразование выполняется в качестве преобразования распаковки. Во время выполнения, если обаT
U
типа и являются типами значений, тоT
иU
обязательно являются одинаковыми и не выполняются преобразования. Во время выполнения, еслиT
это ссылочный тип, тоU
также является ссылочным типом, а преобразование выполняется как явное преобразование ссылок или преобразование удостоверений.
Во всех случаях правила гарантируют, что преобразование выполняется как разблокирование преобразования, если и только если во время выполнения преобразование выполняется из ссылочного типа в тип значения.
Приведенные выше правила не допускают прямого явного преобразования из параметра без ограничений типа в тип, отличный от интерфейса, что может быть удивительно. Причина этого правила заключается в том, чтобы предотвратить путаницу и сделать семантику таких преобразований ясной.
Пример. Рассмотрим следующее объявление:
class X<T> { public static long F(T t) { return (long)t; // Error } }
Если было разрешено прямое преобразование
t
long
в него, можно легко ожидать, чтоX<int>.F(7)
будет возвращено7L
. Однако это не так, поскольку стандартные числовые преобразования учитываются только в том случае, если типы, как известно, числовые во время привязки. Чтобы сделать семантику ясной, приведенный выше пример должен быть написан:class X<T> { public static long F(T t) { return (long)(object)t; // Ok, but will only work when T is long } }
Теперь этот код будет компилироваться, но выполнение
X<int>.F(7)
будет вызывать исключение во время выполнения, так как полеint
не может быть преобразовано непосредственно в объектlong
.пример конца
10.3.9 Определяемые пользователем явные преобразования
Определяемое пользователем явное преобразование состоит из необязательного стандартного явного преобразования, за которым следует выполнение определяемого пользователем неявного или явного оператора преобразования, за которым следует другое необязательное стандартное явное преобразование. Точные правила для оценки определяемых пользователем явных преобразований описаны в разделе 10.5.5.
10.4 Стандартные преобразования
10.4.1 Общие
Стандартные преобразования — это предварительно определенные преобразования, которые могут возникать в рамках определяемого пользователем преобразования.
10.4.2 Стандартные неявные преобразования
Следующие неявные преобразования классифицируются как стандартные неявные преобразования:
- Преобразования удостоверений (§10.2.2)
- Неявные числовые преобразования (§10.2.3)
- Неявные преобразования, допускающие значение NULL (§10.2.6)
- Преобразования литералов NULL (§10.2.7)
- Неявные преобразования ссылок (§10.2.8)
- Преобразования бокса (§10.2.9)
- Преобразования неявных констант (§10.2.11)
- Неявные преобразования с параметрами типа (§10.2.12)
Стандартные неявные преобразования специально исключают неявные преобразования, определенные пользователем.
10.4.3 Стандартные явные преобразования
Стандартные явные преобразования — это все стандартные неявные преобразования, а также подмножество явных преобразований, для которых существует противоположное неявное преобразование.
Примечание. Другими словами, если стандартное неявное преобразование существует из типа в тип
A
B
, то стандартное явное преобразование существует из типа в типA
B
и из типа в типB
A
. конечная заметка
10.5 Определяемые пользователем преобразования
10.5.1 Общие
C# позволяет предварительно определенным неявным и явным преобразованиям дополняться определяемыми пользователем преобразованиями. Определяемые пользователем преобразования вводятся путем объявления операторов преобразования (§15.10.4) в типах классов и структур.
10.5.2 Разрешенные пользовательские преобразования
C# позволяет объявлять только определенные пользовательские преобразования. В частности, невозможно переопределить уже существующее неявное или явное преобразование.
Для заданного исходного типа и целевого типа S
T
, если S
или T
являются типами значений, допускающих значение NULL, давайте S₀
и T₀
ссылаемся на их базовые типы, в противном случае S₀
и T₀
равны и соответственно S
T
. Класс или структуру разрешено объявлять преобразование из исходного типа S
в целевой тип T
, только если все из следующих значений имеют значение true:
-
S₀
иT₀
являются разными типами. -
S₀
ЛибоT₀
класс или тип структуры, в котором происходит объявление оператора. - Ни
S₀
T₀
interface_type. - За исключением определяемых пользователем преобразований преобразование не существует из или из
S
T
T
S
.
Ограничения, применяемые к определяемым пользователем преобразованиям, указаны в разделе 15.10.4.
10.5.3 Оценка определяемых пользователем преобразований
Определяемое пользователем преобразование преобразует исходное выражение, которое может иметь исходный тип, в другой тип, называемый целевым типом. Оценка определяемых пользователем центров преобразования при поиске наиболее конкретного определяемого пользователем оператора преобразования для исходного выражения и целевого типа. Это определение разбито на несколько шагов:
- Поиск набора классов и структур, из которых будут рассматриваться определяемые пользователем операторы преобразования. Этот набор состоит из исходного типа и его базовых классов, если исходный тип существует, а также целевой тип и его базовые классы. Для этого предполагается, что только классы и структуры могут объявлять определяемые пользователем операторы, и что неклассовые типы не имеют базовых классов. Кроме того, если исходный или целевой тип является типом, допускаемым значением NULL, вместо него используется базовый тип.
- Из этого набора типов, определяющих, какие пользовательские и снятые операторы преобразования применимы. Для применимого оператора преобразования можно выполнить стандартное преобразование (§10.4) из исходного выражения в тип операнда оператора, и можно выполнить стандартное преобразование из типа результата оператора в целевой тип.
- Из набора применимых пользовательских операторов, определяющих, какой оператор однозначно является наиболее конкретным. Как правило, наиболее конкретный оператор является оператором, тип операнда которого является "ближайшим" к исходному выражению и тип результата которого является "ближайшим" к целевому типу. Определяемые пользователем операторы преобразования предпочтительнее, чем операторы преобразования, поднятые. Точные правила для установления наиболее конкретного определяемого пользователем оператора преобразования определяются в следующих подклаузах.
После определения наиболее конкретного пользовательского оператора преобразования фактическое выполнение определяемого пользователем преобразования включает до трех шагов:
- Во-первых, при необходимости выполняется стандартное преобразование из исходного выражения в тип операнда определяемого пользователем или оператора преобразования.
- Затем вызов определяемого пользователем или вызываемого оператора преобразования для выполнения преобразования.
- Наконец, при необходимости выполняется стандартное преобразование из типа результата определяемого пользователем оператора преобразования в целевой тип.
Оценка определяемого пользователем преобразования никогда не включает несколько определяемых пользователем или поднятых операторов преобразования. Другими словами, преобразование типа в тип S
никогда не будет сначала выполнять определяемое пользователем преобразование из T
S
и затем выполнять определяемое пользователем преобразование из X
X
.T
- Точные определения оценки определяемых пользователем неявных или явных преобразований приведены в следующих подклаузах. Определения используют следующие термины:
- Если стандартное неявное преобразование (§10.4.2) существует от типа к типу
A
B
, и если ни interface_typeA
B
s
, то, как сообщается, охватывается иA
, как говорятB
,B
охватывается.A
- Если стандартное неявное преобразование (§10.4.2) существует из выражения
E
в типB
, и если ниB
типE
(если он имеет один)s
, тоE
, как сообщается, охватывается и, как сообщаетсяB
, охватывается.B
E
- Наиболее охватывающий тип в наборе типов является один тип, охватывающий все остальные типы в наборе. Если один тип не охватывает все остальные типы, набор не имеет наиболее охватывающего типа. Более интуитивно понятным является наиболее охватывающий тип в наборе — один тип, в который можно неявно преобразовать друг друга.
- Наиболее охватываемый тип в наборе типов — это один тип, охватываемый всеми остальными типами в наборе. Если ни один тип не охватывается всеми другими типами, то набор не имеет наиболее охватываемого типа. Более интуитивно понятным типом является самый маленький тип в наборе— один тип, который может быть неявно преобразован в каждый из остальных типов.
10.5.4 Определяемые пользователем неявные преобразования
Определяемое пользователем неявное преобразование из выражения E
в тип T
обрабатывается следующим образом:
Определите типы
S
иS₀
T₀
.- Если
E
имеет тип, пустьS
он будет таким. - Если
S
илиT
имеют типы значений, допускающие значение NULL, пусть иSᵢ
являются их базовыми типами, в противном случае давайтеTᵢ
Sᵢ
Tᵢ
S
T
и соответствующим образом. - Если
Sᵢ
илиTᵢ
являются параметрами типа, пусть иS₀
будут их эффективными базовыми классами, в противном случае давайтеT₀
S₀
T₀
Sₓ
Tᵢ
и соответственно.
- Если
Найдите набор типов,
D
из которых будут считаться определяемые пользователем операторы преобразования. Этот набор состоитS₀
из (если существует и является классом или структурой), базовыми классамиS₀
(еслиS₀
S₀
существует и является классом), аT₀
(еслиT₀
является классом или структурой). Тип добавляется в наборD
, только если преобразование удостоверений в другой тип, уже включенный в набор, не существует.Найдите набор применимых пользовательских и снятых операторов преобразования.
U
Этот набор состоит из определяемых пользователем и снятых неявных операторов преобразования, объявленных классами или структурами,D
которые преобразуются из типа, охватывающегоE
тип, охватываемый типомT
. ЕслиU
значение пусто, преобразование не определено и возникает ошибка во время компиляции.- Если
S
существует и любой из операторов преобразованияU
S
, то естьSₓ
S
. -
Sₓ
В противном случае является наиболее охватываемый тип в объединенном наборе исходных типов операторов вU
. Если не удается найти ровно один наиболее охватываемый тип, преобразование неоднозначно и возникает ошибка во время компиляции.
- Если
Найдите наиболее конкретный тип целевого объекта ,
Tₓ
операторов вU
:- Если любой из операторов преобразования
U
T
в , тоTₓ
имеет значениеT
. -
Tₓ
В противном случае является наиболее охватывающим типом в объединенном наборе целевых типов операторов вU
. Если не удается найти именно один наиболее охватывающий тип, преобразование неоднозначно и возникает ошибка во время компиляции.
- Если любой из операторов преобразования
Найдите наиболее конкретный оператор преобразования:
- Если
U
содержит ровно один определяемый пользователем оператор преобразования, который преобразуется изSₓ
Tₓ
, то это наиболее конкретный оператор преобразования. - В противном случае, если
U
содержится ровно один оператор преобразования, который преобразуется изSₓ
Tₓ
, то это наиболее конкретный оператор преобразования. - В противном случае преобразование неоднозначно и возникает ошибка во время компиляции.
- Если
Наконец, примените преобразование:
- Если тип E еще не имеет
Sₓ
, то выполняется стандартное неявное преобразование изE
Sₓ
него. - Наиболее конкретный оператор преобразования вызывается для преобразования из
Sₓ
Tₓ
. - Если
Tₓ
нетT
, то выполняется стандартное неявное преобразование изTₓ
T
неявного.
- Если тип E еще не имеет
Определяемое пользователем неявное преобразование типа в тип S
T
существует, если определяемое пользователем неявное преобразование существует из переменной типа S
T
в .
10.5.5 Определяемые пользователем явные преобразования
Определяемое пользователем явное преобразование из выражения E
в тип T
обрабатывается следующим образом:
- Определите типы
S
иS₀
T₀
.- Если
E
имеет тип, пустьS
он будет таким. - Если
S
илиT
имеют типы значений, допускающие значение NULL, пусть иSᵢ
являются их базовыми типами, в противном случае давайтеTᵢ
Sᵢ
Tᵢ
S
T
и соответствующим образом. - Если
Sᵢ
илиTᵢ
являются параметрами типа, пусть иS₀
будут их эффективными базовыми классами, в противном случае давайтеT₀
S₀
T₀
Sᵢ
Tᵢ
и соответственно.
- Если
- Найдите набор типов,
D
из которых будут считаться определяемые пользователем операторы преобразования. Этот набор состоит изS₀
(если существует и является классом или структурой), базовыми классамиS₀
(еслиS₀
S₀
он существует и является классом),T₀
(если это класс или структура), а также базовые классыT₀
(еслиT₀
T₀
это класс).A
Тип добавляется в наборD
, только если преобразование удостоверений в другой тип, уже включенный в набор, не существует. - Найдите набор применимых пользовательских и снятых операторов преобразования.
U
Этот набор состоит из определяемых пользователем и снятых неявных или явных операторов преобразования, объявленных классами или структурами,D
которые преобразуются из типа, охватывающего или охватывающего (если она существует) в тип, охватывающийE
илиS
T
охватываемый. ЕслиU
значение пусто, преобразование не определено и возникает ошибка во время компиляции. - Найдите наиболее конкретный тип источника ,
Sₓ
операторов вU
:- Если S существует и любой из операторов,
U
преобразованных изS
, тоSₓ
имеет значениеS
. - В противном случае, если любой из операторов
U
преобразования из типов,E
охватывающих, являетсяSₓ
наиболее охватывающимся типом в объединенном наборе исходных типов этих операторов. Если наиболее охватываемый тип не найден, преобразование неоднозначно и возникает ошибка во время компиляции. -
Sₓ
В противном случае является наиболее охватывающим типом в объединенном наборе типов источников операторов вU
. Если не удается найти именно один наиболее охватывающий тип, преобразование неоднозначно и возникает ошибка во время компиляции.
- Если S существует и любой из операторов,
- Найдите наиболее конкретный тип целевого объекта ,
Tₓ
операторов вU
:- Если любой из операторов преобразования
U
T
в , тоTₓ
имеет значениеT
. - В противном случае, если любой из операторов
U
преобразования в типы, охватываемыеT
, являетсяTₓ
наиболее охватывающим типом в объединенном наборе целевых типов этих операторов. Если не удается найти именно один наиболее охватывающий тип, преобразование неоднозначно и возникает ошибка во время компиляции. -
Tₓ
В противном случае наиболее охватываемый тип в объединенном наборе целевых типов операторов вU
. Если наиболее охватываемый тип не найден, преобразование неоднозначно и возникает ошибка во время компиляции.
- Если любой из операторов преобразования
- Найдите наиболее конкретный оператор преобразования:
- Если U содержит ровно один определяемый пользователем оператор преобразования, который преобразуется из
Sₓ
Tₓ
, то это наиболее конкретный оператор преобразования. - В противном случае, если
U
содержится ровно один оператор преобразования, который преобразуется изSₓ
Tₓ
, то это наиболее конкретный оператор преобразования. - В противном случае преобразование неоднозначно и возникает ошибка во время компиляции.
- Если U содержит ровно один определяемый пользователем оператор преобразования, который преобразуется из
- Наконец, примените преобразование:
- Если
E
у него еще нет типаSₓ
, выполняется стандартное явное преобразование из ESₓ
. - Наиболее конкретный определяемый пользователем оператор преобразования вызывается для преобразования из
Sₓ
Tₓ
. - Если
Tₓ
нетT
, то выполняется стандартное явное преобразование изTₓ
нееT
.
- Если
Определяемое пользователем явное преобразование типа в тип S
T
существует, если определяемое пользователем явное преобразование существует из переменной типа S
T
в .
10.6 Преобразования с использованием типов, допускающих значение NULL
Преобразования, допускающие значение NULL 10.6.1
Преобразования , допускающие значение NULL, позволяют предварительно определенные преобразования, работающие с типами значений, не допускающими значения NULL, также использоваться с формами, допускающими значение NULL для этих типов. Для каждого предопределенного неявного или явного преобразования, преобразующегося из типа значения, не допускающего значения NULL, в тип S
T
не допускающего значения NULL (§10.2.2, §10.2.3, §10.2.4, §10.2.11, §10.3.2 и §10.3.3) существуют следующие преобразования, допускающие значение NULL:
- Неявное или явное преобразование из
S?
T?
- Неявное или явное преобразование из
S
T?
- Явное преобразование из
S?
T
.
Преобразование, допускающее значение NULL, классифицируется как неявное или явное преобразование.
Некоторые преобразования, допускающие значение NULL, классифицируются как стандартные преобразования и могут возникать в рамках определяемого пользователем преобразования. В частности, все неявные преобразования, допускающие значение NULL, классифицируются как стандартные неявные преобразования (§10.4.2), и эти явные преобразования, допускающие значение NULL, которые удовлетворяют требованиям §10.4.3 , классифицируются как стандартные явные преобразования.
Оценка преобразования, допускающего значение NULL, на основе базового преобразования из S
T
следующего процесса:
- Если преобразование, допускаемое значение NULL, выполняется в
S?
T?
:- Если исходное значение равно NULL (
HasValue
свойствоfalse
равно null), результатом является значение NULL типаT?
. - В противном случае преобразование вычисляется как распаку из в , за которым следует базовое преобразование из
S?
S
, за которым следует оболочка изS
T
.T
T?
- Если исходное значение равно NULL (
- Если преобразование,
S
допускающее значение NULL, выполняется преобразование как базовое преобразование,T?
S
за которым следует оболочка изT
T
.T?
- Если преобразование, допускаемое значение
S?
NULL, выполняется оценка преобразования как распаку сT
S?
последующих базовых преобразований вS
S
.T
10.6.2 Поднятые преобразования
Учитывая определяемый пользователем оператор преобразования, который преобразуется из типа S
значения, не допускающего значения NULL, в тип T
значения, не допускающего значения NULL, существует оператор преобразования , который преобразуется из S?
T?
. Этот оператор преобразования поднимаемого типа выполняет распаку из-за заданного пользователем преобразования, S?
S
S
T
T
T?
за исключением того, что значение NULL преобразуется непосредственно в значение NULL.S?
T?
Оператор преобразования лифта имеет ту же неявную или явную классификацию, что и его базовый определяемый пользователем оператор преобразования.
10.7 Анонимные преобразования функций
10.7.1 Общие
Anonymous_method_expression или lambda_expression классифицируется как анонимная функция (§12.19). Выражение не имеет типа, но может быть неявно преобразовано в совместимый тип делегата. Некоторые лямбда-выражения также могут быть неявно преобразованы в совместимый тип дерева выражений.
В частности, анонимная функция F
совместима с типом D
делегата, предоставленным:
- Если
F
содержит anonymous_function_signature,D
то иF
имеет то же количество параметров. - Если
F
не содержит anonymous_function_signature, тоD
может иметь ноль или более параметров любого типа, если параметрD
не является выходным параметром. - Если
F
имеется явно типизированный список параметров, каждый параметр имеетD
те же модификаторы, что и соответствующий параметр,F
и преобразование удостоверений существует между соответствующим параметром вF
. - Если
F
имеет неявно типизированный список параметров,D
нет ссылочных или выходных параметров. - Если текст
F
выражения является выражением иD
имеет тип возвращаемого значения void или«TaskType»
(§15.15.1F
w.r.t §12), которое будет разрешено в качестве statement_expression (§13.7). - Если текст
F
блока иD
имеет тип возвращаемого значения void илиF
иD
имеет«TaskType»
тип возвращаемого значения, то при указании каждого параметра соответствующего параметраF
вD
текстеF
допустимого блока (w.r.t §13.3), в котором оператор неreturn
указывает выражение. - Если текст выражения является выражением и
F
имеет тип, отличный от асинхронногоF
, илиD
имеет асинхронныйvoid
T
типF
D
и имеет«TaskType»<T>
тип возвращаемого значения (§15.15.1), то при указании каждого параметра соответствующего параметраF
вD
текстеF
допустимого выражения (w.r.t §12), которое неявно преобразуетсяT
в . - Если тело является блоком, а
F
также имеет тип несинхронногоF
и имеет типT
F
D
и«TaskType»<T>
имеет тип возвращаемого значения, то при указании каждого параметра соответствующего параметраF
вD
текстеF
допустимого блока инструкций (w.r.t §13.3) с недоступной конечной точкой, в которой каждая инструкция возврата указывает выражение, которое неявно преобразуетсяT
в .
Пример. В следующих примерах показаны следующие правила:
delegate void D(int x); D d1 = delegate { }; // Ok D d2 = delegate() { }; // Error, signature mismatch D d3 = delegate(long x) { }; // Error, signature mismatch D d4 = delegate(int x) { }; // Ok D d5 = delegate(int x) { return; }; // Ok D d6 = delegate(int x) { return x; }; // Error, return type mismatch delegate void E(out int x); E e1 = delegate { }; // Error, E has an output parameter E e2 = delegate(out int x) { x = 1; }; // Ok E e3 = delegate(ref int x) { x = 1; }; // Error, signature mismatch delegate int P(params int[] a); P p1 = delegate { }; // Error, end of block reachable P p2 = delegate { return; }; // Error, return type mismatch P p3 = delegate { return 1; }; // Ok P p4 = delegate { return "Hello"; }; // Error, return type mismatch P p5 = delegate(int[] a) // Ok { return a[0]; }; P p6 = delegate(params int[] a) // Error, params modifier { return a[0]; }; P p7 = delegate(int[] a) // Error, return type mismatch { if (a.Length > 0) return a[0]; return "Hello"; }; delegate object Q(params int[] a); Q q1 = delegate(int[] a) // Ok { if (a.Length > 0) return a[0]; return "Hello"; };
пример конца
Пример: в следующих примерах используется универсальный тип
Func<A,R>
делегата, представляющий функцию, которая принимает аргумент типаA
и возвращает значение типаR
:delegate R Func<A,R>(A arg);
В заданиях
Func<int,int> f1 = x => x + 1; // Ok Func<int,double> f2 = x => x + 1; // Ok Func<double,int> f3 = x => x + 1; // Error Func<int, Task<int>> f4 = async x => x + 1; // Ok
Параметры и возвращаемые типы каждой анонимной функции определяются из типа переменной, к которой назначается анонимная функция.
Первое назначение успешно преобразует анонимную функцию в тип
Func<int,int>
делегата, так как приx
указании типаint
x + 1
является допустимым выражением, которое неявно преобразуется в типint
.Аналогичным образом, второе назначение успешно преобразует анонимную функцию в тип Func<int,double> , так как результат
x + 1
(int
типа) неявно преобразуется в типdouble
.Однако третье назначение является ошибкой во время компиляции, так как, если
x
задан типdouble
, результатx + 1
(типаdouble
) неявно преобразуется в типint
.Четвертое назначение успешно преобразует анонимную асинхронную функцию в тип
Func<int, Task<int>>
делегата, так как результатx + 1
(типаint
) неявно преобразуется в действующий типint
возвращаемой лямбда-асинхронной функции, которая имеет возвращаемый типTask<int>
.пример конца
Лямбда-выражение F
совместимо с типом Expression<D>
дерева выражений, если F
совместим с типом D
делегата. Это не относится к анонимным методам, только лямбда-выражениям.
Анонимные функции могут влиять на разрешение перегрузки и участвовать в выводе типов. Дополнительные сведения см. в разделе "12.6 ".
10.7.2. Оценка анонимных преобразований функций в типы делегатов
Преобразование анонимной функции в тип делегата создает экземпляр делегата, ссылающийся на анонимную функцию и набор (возможно, пустой) захваченных внешних переменных, которые активны во время оценки. При вызове делегата выполняется текст анонимной функции. Код в тексте выполняется с помощью набора захваченных внешних переменных, на которые ссылается делегат. Delegate_creation_expression (§12.8.17.6) можно использовать в качестве альтернативного синтаксиса для преобразования анонимного метода в тип делегата.
Список вызовов делегата, созданный из анонимной функции, содержит одну запись. Точный целевой объект и целевой метод делегата не определены. В частности, не указано, является null
ли целевой объект делегата, this
значением включающего элемента функции или другим объектом.
Преобразования семантически идентичных анонимных функций с одинаковым (возможно пустым) набором захваченных экземпляров внешних переменных в те же типы делегатов разрешены (но не требуются) для возврата одного экземпляра делегата. Термин семантически идентичен здесь, чтобы означать, что выполнение анонимных функций в всех случаях будет производить одинаковые эффекты, учитывая одни и те же аргументы. Это правило позволяет оптимизировать код, например приведенный ниже.
delegate double Function(double x);
class Test
{
static double[] Apply(double[] a, Function f)
{
double[] result = new double[a.Length];
for (int i = 0; i < a.Length; i++)
{
result[i] = f(a[i]);
}
return result;
}
static void F(double[] a, double[] b)
{
a = Apply(a, (double x) => Math.Sin(x));
b = Apply(b, (double y) => Math.Sin(y));
...
}
}
Так как два анонимных делегата функции имеют один и тот же (пустой) набор захваченных внешних переменных, и так как анонимные функции семантически идентичны, компилятор может ссылаться на тот же целевой метод. Действительно, компилятору разрешено возвращать тот же экземпляр делегата из обоих анонимных функциональных выражений.
10.7.3. Оценка преобразования лямбда-выражений в типы дерева выражений
Преобразование лямбда-выражения в тип дерева выражений создает дерево выражений (§8.6). Точнее, оценка преобразования лямбда-выражения создает структуру объекта, представляющую структуру лямбда-выражения.
Не все лямбда-выражения можно преобразовать в типы дерева выражений. Преобразование в совместимый тип делегата всегда существует, но оно может завершиться ошибкой во время компиляции по причинам, определенным реализацией.
Примечание. Ниже приведены распространенные причины, по которым лямбда-выражение не удалось преобразовать в тип дерева выражений:
- Он имеет блок тела
- Он имеет
async
модификатор- Он содержит оператор назначения
- Он содержит выходной или ссылочный параметр
- Он содержит динамически привязанное выражение
конечная заметка
Преобразования групп методов 10.8
Неявное преобразование существует из группы методов (§12.2) в совместимый тип делегата (§20.4). Если D
является типом делегата и E
является выражением, классифицируемым как группа методов, то D
совместимо с E
тем, если E
он содержит по крайней мере один метод, применимый в обычной форме (§12.6.4.2) к любому списку аргументов (§12.6.2) с типами и модификаторамиD
, соответствующими типам параметров и модификаторам, как описано ниже.
Приложение времени компиляции преобразования из группы E
методов в тип D
делегата описано ниже.
- Один метод выбирается в соответствии с вызовом метода
M
(§12.8.10.2) формыE(A)
со следующими изменениями:- Список
A
аргументов — это список выражений, каждый классифицируемый как переменная, а также тип и модификатор (in
,out
илиref
) соответствующего параметра вD
, за исключением параметров типаdynamic
, где соответствующее выражение имеет типobject
вместоdynamic
. - Методы-кандидаты считаются только теми методами, которые применимы в обычной форме и не пропускают необязательные параметры (§12.6.4.2). Таким образом, методы кандидатов игнорируются, если они применимы только в развернутой форме, или если один или несколько их необязательных параметров не имеют соответствующего параметра в
D
.
- Список
- Преобразование считается существующим, если алгоритм §12.8.10.2
D
- Если выбранный метод является методом
M
экземпляра, выражение экземпляра, связанное сE
определением целевого объекта делегата. - Если выбранный метод является методом
M
расширения, который обозначается с помощью доступа к члену в выражении экземпляра, то это выражение экземпляра определяет целевой объект делегата. - Результатом преобразования является значение типа
D
, а именно делегат, ссылающийся на выбранный метод и целевой объект.
Пример. Ниже показаны преобразования групп методов:
delegate string D1(object o); delegate object D2(string s); delegate object D3(); delegate string D4(object o, params object[] a); delegate string D5(int i); class Test { static string F(object o) {...} static void G() { D1 d1 = F; // Ok D2 d2 = F; // Ok D3 d3 = F; // Error – not applicable D4 d4 = F; // Error – not applicable in normal form D5 d5 = F; // Error – applicable but not compatible } }
Назначение, которое
d1
неявно преобразует группуF
методов в значение типаD1
.Назначение, показывающее
d2
, как можно создать делегат для метода, который имеет менее производные (контравариантные) типы параметров и более производный (ковариантный) тип возвращаемого значения.Назначение, показыв,
d3
как не существует преобразования, если метод неприменимо.Назначение, показываемое
d4
, как метод должен применяться в обычной форме.Назначение, показыв,
d5
как параметр и возвращаемые типы делегата и метода могут отличаться только для ссылочных типов.пример конца
Как и во всех других неявных и явных преобразованиях, оператор приведения можно использовать для явного выполнения определенного преобразования.
Пример. Таким образом, пример
object obj = new EventHandler(myDialog.OkClick);
вместо этого может быть записано
object obj = (EventHandler)myDialog.OkClick;
пример конца
Преобразование группы методов может ссылаться на универсальный метод, явно указывая аргументы типа в пределах E
или с помощью вывода типа (§12.6.3). Если используется вывод типов, типы параметров делегата используются в качестве типов аргументов в процессе вывода. Возвращаемый тип делегата не используется для вывода. Указываются ли аргументы типа или выводятся, они являются частью процесса преобразования группы методов; это аргументы типа, используемые для вызова целевого метода при вызове результирующего делегата.
Пример:
delegate int D(string s, int i); delegate int E(); class X { public static T F<T>(string s, T t) {...} public static T G<T>() {...} static void Main() { D d1 = F<int>; // Ok, type argument given explicitly D d2 = F; // Ok, int inferred as type argument E e1 = G<int>; // Ok, type argument given explicitly E e2 = G; // Error, cannot infer from return type } }
пример конца
Группы методов могут влиять на разрешение перегрузки и участвовать в выводе типов. Дополнительные сведения см. в разделе "12.6 ".
Оценка времени выполнения преобразования группы методов выполняется следующим образом:
- Если метод, выбранный во время компиляции, является методом экземпляра или методом расширения, доступ к которому осуществляется в качестве метода экземпляра, целевой объект делегата определяется из выражения экземпляра, связанного с
E
:- Выражение экземпляра вычисляется. Если эта оценка вызывает исключение, дальнейшие действия не выполняются.
- Если выражение экземпляра имеет reference_type, значение, вычисляемое выражением экземпляра, становится целевым объектом. Если выбранный метод является методом экземпляра и целевым объектом является
null
,System.NullReferenceException
создается исключение, и дальнейшие шаги не выполняются. - Если выражение экземпляра имеет value_type, операция бокса (§10.2.9) выполняется для преобразования значения в объект, и этот объект становится целевым объектом.
- В противном случае выбранный метод является частью вызова статического метода, а целевой объект делегата .
null
- Экземпляр делегата типа
D
делегата получается со ссылкой на метод, который был определен во время компиляции, и ссылка на целевой объект, вычисляемый выше, как показано ниже.- Преобразованию разрешено (но не обязательно) использовать существующий экземпляр делегата, который уже содержит эти ссылки.
- Если существующий экземпляр не использовался повторно, создается новый экземпляр (§20.5). Если для выделения нового экземпляра недостаточно памяти,
System.OutOfMemoryException
создается исключение. В противном случае экземпляр инициализирован с заданными ссылками.
ECMA C# draft specification