Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
10.1 Общие
Преобразование приводит к преобразованию выражения в определенный тип или его обработке; в предыдущем случае преобразование может привести к изменению представления. Преобразования могут быть неявными или явными, и это определяет, требуется ли явное приведение.
Пример. Например, преобразование типа в тип неявно, поэтому выражения типа
intlongintмогут неявно рассматриваться как тип.longПротивоположное преобразование, от типа к типуlongint, является явным и поэтому требуется явное приведение.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.8) и назначения (§12.23).
Предварительно определенные неявные преобразования всегда успешно и никогда не вызывают возникновения исключений.
Примечание. Правильно разработанные пользователем неявные преобразования должны также демонстрировать эти характеристики. конечная заметка
В целях преобразования типы object и dynamic являются преобразованными удостоверениями (§10.2.2).
Однако динамические преобразования (§10.2.10dynamic).
Преобразование удостоверений 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иt2t3все имеют два элемента:intза которым следуетstring. Типы элементов кортежа могут сами по себе кортежами, как и вt4,t5иt6. Преобразование удостоверений существует между каждой парой соответствующих типов элементов, включая вложенные кортежи, поэтому преобразование удостоверений существует между типами кортежейt4иt5t6.пример конца
Все преобразования удостоверений являются симметричными. Если преобразование удостоверений существует из T₁T₂, преобразование удостоверений существует из T₂T₁. Два типа — это преобразование удостоверений, когда преобразование удостоверений существует между двумя типами.
В большинстве случаев преобразование удостоверений не влияет на среду выполнения. Однако, так как операции с плавающей запятой могут выполняться с более высокой точностью, чем предписано их типом (§8.3.7), назначение их результатов может привести к потере точности, и явные приведения гарантированно снижают точность до того, что предписано типом (§12.9.8).
10.2.3 Неявные числовые преобразования
Неявные числовые преобразования:
- От
sbyte, до,short,intlong,floatилиdouble.decimal -
byteshortushortintuintlongulongfloatОтdouble, или .decimal - От
short, доint,long,floatdoubleилиdecimal. - От , , ,
ushortintuintlongили .ulongfloatdoubledecimal - От
int, доlong,floatdoubleилиdecimal. - От
uint, доlong,ulong,floatdoubleилиdecimal. -
longОтfloatдо ,doubleилиdecimal. -
ulongОтfloatдо ,doubleилиdecimal. -
charushortintuintlongulongfloatОтdouble, или .decimal - С
floatнаdouble.
Преобразования из , intuint или longulong из floatили из long или ulongdouble могут привести к потере точности, но никогда не приведет к потере величины. Другие неявные числовые преобразования никогда не теряют никаких сведений.
Не существует предопределенных неявных преобразований в char тип, поэтому значения других целочисленных типов не преобразуются char в тип автоматически.
Преобразования неявного перечисления 10.2.4
Преобразование неявного перечисления позволяет constant_expression (§12.25) с любым целым типом и нулевым значением, преобразованным в любые 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 до любой предоставляется
Sпроизводный от . - От любого class_type до любой , предоставленной
Sреализации. - От любого interface_type до любой предоставляется
Sпроизводный от . -
SSᵢс типом элемента, если все следующие значения имеют значение true:-
SиTотличаются только в типе элемента. Другими словами,SиTимеют то же количество измерений. - Неявное преобразование ссылок существует из
SᵢTᵢ.
-
- Из одномерного типа
S[]System.Collections.Generic.IList<T>System.Collections.Generic.IReadOnlyList<T>массива в , а также их базовые интерфейсы, при условии, что существует неявное удостоверение или преобразование ссылок из .ST - Из любого array_type
System.Arrayи интерфейсов, которые он реализует. - Из любого delegate_type
System.Delegateв интерфейсы, которые он реализует. - От литерала NULL (§6.4.5.7) до любого ссылочного типа.
- От любого , если он имеет неявное удостоверение или преобразование ссылок на reference_type
Tи имеет преобразованиеT₀удостоверений в . - От любого reference_type к интерфейсу или типу
Tделегата, если он имеет неявное удостоверение или преобразование ссылок на интерфейс или типT₀делегата иT₀является вариативным (§19.2.3.3) в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₀удостоверения в . - От любого non_nullable_value_type до любого interface_type
Iтаким образом, что преобразование бокса из non_nullable_value_type в другой interface_typeI₀, иI₀является вариативным (§19.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Преобразование бокса подразумевает создание копии прямоугольного значения. Это отличается от преобразования . Например, ниже
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 на консоли, так как неявная операция бокса, возникающая в назначении,
pboxприводит к копированию значения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Назначения и
s2iоба используют неявные динамические преобразования, где привязка операций приостановлена до времени выполнения. Во время выполнения неявные преобразования ищутся из типаdвремени выполнения (string) в целевой тип. Преобразование найдено,stringно не вint.пример конца
Преобразования неявных константных выражений 10.2.11
Неявное преобразование константного выражения позволяет выполнить следующие преобразования:
-
Constant_expression (§12.25) типа можно преобразовать в тип
intsbyte, ,byte,shortushortuintилиulong, если значение constant_expression находится в диапазоне целевого типа. - Constant_expression типа можно преобразовать в тип
long, если значениеulongне является отрицательным.
10.2.12 Неявные преобразования с параметрами типа
, который, как известно, является ссылочным типом (§15.2.5), существуют следующие неявные преобразования ссылок (T§10.2.8):
- От
Tдо эффективного базового классаC, отTлюбого базового класса до любого базового классаCи отTлюбого интерфейса, реализованногоC. - От
Tinterface_typeнаборе интерфейсов и отIлюбого базового интерфейсаT. - От
Tпараметра типа,Uуказанного в зависимости отT(U).Примечание. Поскольку
Tв пределах областиTдействия тип времениUвыполнения всегда будет ссылочным типом, даже еслиUон не является ссылочным типом во время компиляции. конечная заметка - От литерала NULL (§6.4.5.7) до T.
, который T является ссылочным типом §15.2.5, в время компиляции рассматриваются следующие преобразования, связанные с боксом (T). Во время выполнения, если T это тип значения, преобразование выполняется как преобразование бокса. Во время выполнения, если T это ссылочный тип, преобразование выполняется как неявное преобразование ссылок или преобразование удостоверений.
- От
Tдо эффективного базового классаC, отTлюбого базового класса до любого базового классаCи отTлюбого интерфейса, реализованногоC.Примечание.
CБудет одним из типовSystem.ObjectилиSystem.ValueType(вSystem.Enumпротивном случаеTможет быть ссылочным типом). конечная заметка - От
Tinterface_typeнаборе интерфейсов и отIлюбого базового интерфейсаT.
Для type_parameterT, не известного как ссылочный тип, существует неявное преобразование из T указанного U параметра типа в зависимости от Tуказанного параметраU. Во время выполнения, если T является типом значения и U является ссылочным типом, преобразование выполняется как преобразование бокса. Во время выполнения, если оба TU типа и являются типами значений, то T и U обязательно являются одинаковыми и не выполняются преобразования. Во время выполнения, если T это ссылочный тип, то U также является ссылочным типом, а преобразование выполняется как неявное преобразование ссылок или преобразование удостоверений (§15.2.5).
Для заданного параметра Tтипа существуют следующие дальнейшие неявные преобразования:
- От
Tтипа ссылки, если он имеет неявное преобразование в ссылочный типSS₀иS₀имеет преобразованиеSудостоверений в . Во время выполнения преобразование выполняется так же, как и преобразованиеS₀в . - От
TтипаIинтерфейса, если он имеет неявное преобразование в типI₀интерфейса, иI₀имеет дисперсию преобразование вI(§19.2.3.3). Во время выполнения, еслиTэто тип значения, преобразование выполняется как преобразование бокса. В противном случае преобразование выполняется как неявное преобразование ссылок или преобразование удостоверений.
Во всех случаях правила гарантируют, что преобразование выполняется как преобразование в бокс, если и только если во время выполнения преобразование выполняется из типа значения в ссылочный тип.
10.2.13 Неявные преобразования кортежей
Неявное преобразование существует из выражения E кортежа в тип T кортежа, если E имеет ту же arity, что T и неявное преобразование существует из каждого элемента в E соответствующий тип элемента.T Преобразование выполняется путем создания экземпляра соответствующего TSystem.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,t2t4иt5все допустимые, так как неявные преобразования существуют из выражений элементов в соответствующие типы элементов. Объявлениеt3недопустимо, так как преобразование не выполняется.nullintОбъявление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.2.18
Существует неявное преобразование из switch_expression (§12.11) в каждый типT, для которого существует неявное преобразование от каждого switch_expression_arm switch_expression_arm_expression в .T
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.8).
Набор явных преобразований включает все неявные преобразования.
Примечание. Например, это позволяет явно использовать приведение при наличии неявного преобразования удостоверений, чтобы принудительно выбрать перегрузку определенного метода. конечная заметка
Явные преобразования, которые не являются неявными преобразованиями, являются преобразованиями, которые не могут быть проверены всегда для успешного выполнения, преобразования, которые, возможно, теряют информацию, и преобразования между доменами типов достаточно отличаются, чтобы заслуживают явной нотации.
10.3.2 Явные числовые преобразования
Явные числовые преобразования — это преобразования из numeric_type в другую numeric_type, для которой неявное числовое преобразование (§10.2.3) еще не существует:
- От
sbyte, доbyte,ushort,uintulongилиchar. -
byteОтsbyteилиchar. - От
short, до,sbyte,byteushort,uintилиulong.char - От
ushort, доsbyte,byteshortилиchar. - От , , ,
intsbytebyteshortили .ushortuintulongchar - От
uint, до,sbyte,byteshort,ushortилиint.char -
longsbytebyteshortushortintuintОтulong, или .char -
ulongsbytebyteshortushortintuintОтlong, или .char -
charОтsbyteдо ,byteилиshort. -
floatsbytebyteshortushortintuintlongulongОтchar, или .decimal -
doublesbytebyteshortushortintuintlongulongcharОтfloat, или .decimal -
decimalsbytebyteshortushortintuintlongulongcharОтfloat, или .double
Так как явные преобразования включают все неявные и явные числовые преобразования, всегда можно преобразовать из любого numeric_type в любой другой numeric_type с помощью выражения приведения (§12.9.8).
Явные числовые преобразования, возможно, теряют информацию или могут привести к возникновению исключений. Явное числовое преобразование обрабатывается следующим образом:
- Для преобразования целочисленного типа в другой целочисленный тип обработка зависит от контекста проверки переполнения (§12.8.20), в котором происходит преобразование:
-
checkedВ контексте преобразование завершается успешно, если значение исходного операнда находится в диапазоне целевого типа, но вызываетSystem.OverflowExceptionисключение, если значение исходного операнда выходит за пределы диапазона целевого типа. - В контексте
uncheckedпреобразование всегда выполняется успешно и выполняется следующим образом.- Если исходный тип больше целевого типа, то исходное значение усечено путем отмены его "дополнительных" наиболее значимых битов. Результат затем обрабатывается как значение целевого типа.
- Если исходный тип совпадает с типом назначения, то исходное значение рассматривается как значение целевого типа.
-
- Для преобразования из
decimalцелочисленного типа исходное значение округляется до нуля до ближайшего целочисленного значения, и это целочисленное значение становится результатом преобразования. Если результирующее целочисленное значение выходит за пределы диапазона целевого типа,System.OverflowExceptionсоздается исключение. - Для преобразования из
floatцелочисленногоdoubleтипа обработка зависит от контекста проверки переполнения (§12.8.20), в котором выполняется преобразование:- В проверенном контексте преобразование выполняется следующим образом:
- Если значение операнда равно NaN или бесконечно,
System.OverflowExceptionсоздается исключение. - В противном случае исходный операнд округляется до нуля до ближайшего целочисленного значения. Если это целочисленное значение находится в диапазоне целевого типа, это значение является результатом преобразования.
- В противном случае возникает исключение
System.OverflowException.
- Если значение операнда равно NaN или бесконечно,
- В незаверяемом контексте преобразование всегда выполняется успешно и выполняется следующим образом.
- Если значение операнда равно NaN или бесконечно, результат преобразования является неопределенным значением целевого типа.
- В противном случае исходный операнд округляется до нуля до ближайшего целочисленного значения. Если это целочисленное значение находится в диапазоне целевого типа, это значение является результатом преобразования.
- В противном случае результат преобразования является неопределенным значением целевого типа.
- В проверенном контексте преобразование выполняется следующим образом:
- Для преобразования из
doublefloatв ,doubleзначение округляется до ближайшегоfloatзначения.doubleЕсли значение слишком мало для представления в видеfloat, результат становится ноль с тем же знаком, что и значение. Если величинаdoubleзначения слишком велика для представления в видеfloat, результат становится бесконечностью с тем же знаком, что и значение.doubleЕсли значение равно NaN, результат также является NaN. - Для преобразования из
floatилиdoubledecimalв , исходное значение преобразуется вdecimalпредставление и округляется до ближайшего числа при необходимости (§8.3.8).- Если исходное значение слишком мало для представления в виде
decimal, результат становится нулевым, сохраняя знак исходного значения, еслиdecimalподдерживает подписанные нулевые значения. - Если величина исходного значения слишком велика, чтобы представить как
decimalбесконечность, результатом является бесконечность, сохраняющая знак исходного значения, если десятичное представление поддерживает бесконечность; в противном случае создается System.OverflowException. - Если исходное значение равно NaN, результатом является NaN, если десятичное представление поддерживает NaNs; в противном случае возникает исключение System.OverflowException.
- Если исходное значение слишком мало для представления в виде
- Для преобразования из
decimalfloatилиdouble,decimalзначение округляется до ближайшегоdoubleилиfloatзначения. Если величина исходного значения слишком велика, чтобы представить в целевом типе или значение бесконечности, результатом является бесконечность, сохраняющая знак исходного значения. Если исходное значение равно NaN, результатом является NaN. Хотя это преобразование может потерять точность, это никогда не приводит к возникновению исключения.
Примечание. Тип
decimalне требуется для поддержки определенных значений или значенийfloatNaN, но может сделать это; его диапазон может быть меньше диапазона иdouble, но не гарантируется. Дляdecimalпредставлений без определенных значений или значений NaN и с диапазоном меньше, чемfloatрезультат преобразования изdecimalлюбогоfloatилиdoubleникогда не будет бесконечностью или NaN. конечная заметка
10.3.3 Явные преобразования перечисления
Явные преобразования перечисления:
- От
sbyte,byte,shortushortintuintlongulongcharfloatdoubleилиdecimalлюбой enum_type. - От любого
sbytebyteshortushortintuintдо , или . - От любого enum_type до любого другого 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 до любого предоставляется
Sбазовый класс. - От любого class_type до любой
Sпредоставляется не запечатанный и предоставленныйTне реализуется.S - От любого interface_type до любого
SпредоставляетсяTне запечатанный или предоставленныйTTреализации. - От любого interface_type до любой предоставляется
Sне является производным от. -
SSᵢс типом элемента, если все следующие значения имеют значение true:-
SиTотличаются только в типе элемента. Другими словами,SиTимеют то же количество измерений. - Явное преобразование ссылок существует из
SᵢTᵢ.
-
- От
System.Arrayи интерфейсов, которые он реализует, в любой array_type. - Из одномерной в , а также его базовые интерфейсы, при условии, что имеется преобразование удостоверений или явное преобразование ссылок в .
S[]System.Collections.Generic.IList<T> - От
System.Collections.Generic.IList<S>,System.Collections.Generic.IReadOnlyList<S>и их базовых интерфейсов к одномерным типуT[]массива, при условии, что имеется преобразование удостоверений или явное преобразование ссылок изST. - От
System.Delegateи интерфейсов, которые он реализует в любой delegate_type. - Из ссылочного типа в ссылочный тип
S, если он имеет явное преобразование ссылок изTссылочного типа в ссылочный типSиT₀имеется преобразование удостоверений вT₀T₀.T - От ссылочного типа
Sк интерфейсу или типуTделегата, если он имеет явное преобразование ссылок изSинтерфейса или делегатаT₀и либоT₀является вариативным или преобразуемымTTT₀в §19.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₀ - От любого interface_type до любой
I, в которой происходит распаковка преобразования из interface_type в non_nullable_value_typeI₀иI₀либо variance_convertible вIилиIявляется вариативнымI₀преобразованием (§19.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 Явные преобразования с параметрами типа
, который, как известно, является ссылочным типом (§15.2.5T§10.3.5):
- От эффективного
Cбазового классаTкTлюбому базовому классу и от любого базового классаCTдо . - От любого interface_type до
T. - От
Tлюбого interface_typeI, предоставленного, еще не выполняется неявное преобразование ссылок вTI. -
,
UкотороеTзависит отT(U).Примечание. Поскольку
Tв пределах областиTдействия тип времени выполнения всегда будет ссылочным типом, даже еслиUон не является ссылочным типом во время компиляции. конечная заметка
, который T является ссылочным типом (§15.2.5), следующие преобразования, связанные с компиляцией, считаются распаковкой преобразований (T). Во время выполнения, если T это тип значения, преобразование выполняется в виде преобразования распаковки. Во время выполнения, если T это ссылочный тип, преобразование выполняется как явное преобразование ссылок или преобразование удостоверений.
- От эффективного
Cбазового классаTкTлюбому базовому классу и от любого базового классаCTдо .Примечание. C будет одним из типов
System.Object,System.ValueTypeили (вSystem.Enumпротивном случаеTможет быть ссылочным типом). конечная заметка - От любого interface_type до
T.
, который T известен как ссылочный тип (§15.2.5), существуют следующие явные преобразования:
- От
Tлюбого interface_typeI, предоставленного, еще не выполняется неявное преобразование изTI. Это преобразование состоит из неявного преобразования бокса (§10.2.9),Tobjectза которым следует явное преобразование ссылок вobjectI. При выполнении, еслиTэто тип значения, преобразование выполняется как преобразование в поле, за которым следует явное преобразование ссылок. Во время выполнения, еслиTэто ссылочный тип, преобразование выполняется как явное преобразование ссылок. - От параметра
Uтипа, указанногоTвTзависимости отU(§15.2.5). Во время выполнения, еслиTэто тип значения иUявляется ссылочным типом, преобразование выполняется в качестве преобразования распаковки. Во время выполнения, если обаTUтипа и являются типами значений, тоTиUобязательно являются одинаковыми и не выполняются преобразования. Во время выполнения, еслиTэто ссылочный тип, тоUтакже является ссылочным типом, а преобразование выполняется как явное преобразование ссылок или преобразование удостоверений.
Во всех случаях правила гарантируют, что преобразование выполняется как разблокирование преобразования, если и только если во время выполнения преобразование выполняется из ссылочного типа в тип значения.
Приведенные выше правила не допускают прямого явного преобразования из параметра без ограничений типа в тип, отличный от интерфейса, что может быть удивительно. Причина этого правила заключается в том, чтобы предотвратить путаницу и сделать семантику таких преобразований ясной.
Пример. Рассмотрим следующее объявление:
class X<T> { public static long F(T t) { return (long)t; // Error } }Если было разрешено прямое преобразование
tlongв него, можно легко ожидать, что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 Стандартные явные преобразования
Стандартные явные преобразования — это все стандартные неявные преобразования, а также подмножество явных преобразований, для которых существует противоположное неявное преобразование.
Примечание. Другими словами, если стандартное неявное преобразование существует из типа в тип
AB, то стандартное явное преобразование существует из типа в типABи из типа в типBA. конечная заметка
10.5 Определяемые пользователем преобразования
10.5.1 Общие
C# позволяет предварительно определенным неявным и явным преобразованиям дополняться определяемыми пользователем преобразованиями. Определяемые пользователем преобразования вводятся путем объявления операторов преобразования (§15.10.4) в типах классов и структур.
10.5.2 Разрешенные пользовательские преобразования
C# позволяет объявлять только определенные пользовательские преобразования. В частности, невозможно переопределить уже существующее неявное или явное преобразование.
Для заданного исходного типа и целевого типа ST, если S или T являются типами значений, допускающих значение NULL, давайте S₀ и T₀ ссылаемся на их базовые типы, в противном случае S₀ и T₀ равны и соответственно ST . Класс или структуру разрешено объявлять преобразование из исходного типа S в целевой тип T , только если все из следующих значений имеют значение true:
-
S₀иT₀являются разными типами. -
S₀ЛибоT₀класс или тип структуры, в котором происходит объявление оператора. - Ни
S₀T₀interface_type. - За исключением определяемых пользователем преобразований преобразование не существует из или из
STTS.
Ограничения, применяемые к определяемым пользователем преобразованиям, указаны в разделе 15.10.4.
10.5.3 Оценка определяемых пользователем преобразований
Определяемое пользователем преобразование преобразует исходное выражение, которое может иметь исходный тип, в другой тип, называемый целевым типом. Оценка определяемых пользователем центров преобразования при поиске наиболее конкретного определяемого пользователем оператора преобразования для исходного выражения и целевого типа. Это определение разбито на несколько шагов:
- Поиск набора классов и структур, из которых будут рассматриваться определяемые пользователем операторы преобразования. Этот набор состоит из исходного типа и его базовых классов, если исходный тип существует, а также целевой тип и его базовые классы. Для этого предполагается, что только классы и структуры могут объявлять определяемые пользователем операторы, и что неклассовые типы не имеют базовых классов. Кроме того, если исходный или целевой тип является типом, допускаемым значением NULL, вместо него используется базовый тип.
- Из этого набора типов, определяющих, какие пользовательские и снятые операторы преобразования применимы. Для применимого оператора преобразования можно выполнить стандартное преобразование (§10.4) из исходного выражения в тип операнда оператора, и можно выполнить стандартное преобразование из типа результата оператора в целевой тип.
- Из набора применимых пользовательских операторов, определяющих, какой оператор однозначно является наиболее конкретным. Как правило, наиболее конкретный оператор является оператором, тип операнда которого является "ближайшим" к исходному выражению и тип результата которого является "ближайшим" к целевому типу. Определяемые пользователем операторы преобразования предпочтительнее, чем операторы преобразования, поднятые. Точные правила для установления наиболее конкретного определяемого пользователем оператора преобразования определяются в следующих подклаузах.
После определения наиболее конкретного пользовательского оператора преобразования фактическое выполнение определяемого пользователем преобразования включает до трех шагов:
- Во-первых, при необходимости выполняется стандартное преобразование из исходного выражения в тип операнда определяемого пользователем или оператора преобразования.
- Затем вызов определяемого пользователем или вызываемого оператора преобразования для выполнения преобразования.
- Наконец, при необходимости выполняется стандартное преобразование из типа результата определяемого пользователем оператора преобразования в целевой тип.
Оценка определяемого пользователем преобразования никогда не включает несколько определяемых пользователем или поднятых операторов преобразования. Другими словами, преобразование типа в тип S никогда не будет сначала выполнять определяемое пользователем преобразование из TS и затем выполнять определяемое пользователем преобразование из XX .T
- Точные определения оценки определяемых пользователем неявных или явных преобразований приведены в следующих подклаузах. Определения используют следующие термины:
- Если стандартное неявное преобразование (§10.4.2) существует от типа
Aк типуB, и если ниA, ниBне являются интерфейсными типами, то говорят, чтоAохватываетсяB, аBохватываетA. - Если стандартное неявное преобразование (§10.4.2) существует из выражения
Eв типB, и если ниB, ни типE(если он имеет) interface_type, тоE, как утверждается, охватываетсяB, иB, как говорят, охватываетE. - Наиболее охватывающий тип в наборе типов является один тип, охватывающий все остальные типы в наборе. Если один тип не охватывает все остальные типы, набор не имеет наиболее охватывающего типа. Более интуитивно понятным является наиболее охватывающий тип в наборе — один тип, в который можно неявно преобразовать друг друга.
- Наиболее охватываемый тип в наборе типов — это один тип, охватываемый всеми остальными типами в наборе. Если ни один тип не охватывается всеми другими типами, то набор не имеет наиболее охватываемого типа. Более интуитивно понятным типом является самый маленький тип в наборе— один тип, который может быть неявно преобразован в каждый из остальных типов.
10.5.4 Определяемые пользователем неявные преобразования
Определяемое пользователем неявное преобразование из выражения E в тип T обрабатывается следующим образом:
Определите типы
SиS₀T₀.- Если
Eимеет тип, пустьSон будет таким. - Если
SилиTимеют типы значений, допускающие значение NULL, пусть иSᵢявляются их базовыми типами, в противном случае давайтеTᵢSᵢTᵢSTи соответствующим образом. - Если
SᵢилиTᵢявляются параметрами типа, пусть иS₀будут их эффективными базовыми классами, в противном случае давайтеT₀S₀T₀SₓTᵢи соответственно.
- Если
Найдите набор типов,
Dиз которых будут считаться определяемые пользователем операторы преобразования. Этот набор состоитS₀из (если существует и является классом или структурой), базовыми классамиS₀(еслиS₀S₀существует и является классом), аT₀(еслиT₀является классом или структурой). Тип добавляется в наборD, только если преобразование удостоверений в другой тип, уже включенный в набор, не существует.Найдите набор применимых пользовательских и снятых операторов преобразования.
UЭтот набор состоит из определяемых пользователем и снятых неявных операторов преобразования, объявленных классами или структурами,Dкоторые преобразуются из типа, охватывающегоEтип, охватываемый типомT. ЕслиUзначение пусто, преобразование не определено и возникает ошибка во время компиляции.- Если
Sсуществует и любой из операторов преобразованияUS, то естьSₓS. -
SₓВ противном случае является наиболее охватываемый тип в объединенном наборе исходных типов операторов вU. Если не удается найти ровно один наиболее охватываемый тип, преобразование неоднозначно и возникает ошибка во время компиляции.
- Если
Найдите наиболее конкретный тип целевого объекта ,
Tₓоператоров вU:- Если любой из операторов преобразования
UTв , тоTₓимеет значениеT. -
TₓВ противном случае является наиболее охватывающим типом в объединенном наборе целевых типов операторов вU. Если не удается найти именно один наиболее охватывающий тип, преобразование неоднозначно и возникает ошибка во время компиляции.
- Если любой из операторов преобразования
Найдите наиболее конкретный оператор преобразования:
- Если
Uсодержит ровно один определяемый пользователем оператор преобразования, который преобразуется изSₓTₓ, то это наиболее конкретный оператор преобразования. - В противном случае, если
Uсодержится ровно один оператор преобразования, который преобразуется изSₓTₓ, то это наиболее конкретный оператор преобразования. - В противном случае преобразование неоднозначно и возникает ошибка во время компиляции.
- Если
Наконец, примените преобразование:
- Если тип E еще не имеет
Sₓ, то выполняется стандартное неявное преобразование изESₓнего. - Наиболее конкретный оператор преобразования вызывается для преобразования из
SₓTₓ. - Если
TₓнетT, то выполняется стандартное неявное преобразование изTₓTнеявного.
- Если тип E еще не имеет
Определяемое пользователем неявное преобразование типа в тип ST существует, если определяемое пользователем неявное преобразование существует из переменной типа STв .
10.5.5 Определяемые пользователем явные преобразования
Определяемое пользователем явное преобразование из выражения E в тип T обрабатывается следующим образом:
- Определите типы
SиS₀T₀.- Если
Eимеет тип, пустьSон будет таким. - Если
SилиTимеют типы значений, допускающие значение NULL, пусть иSᵢявляются их базовыми типами, в противном случае давайтеTᵢSᵢTᵢSTи соответствующим образом. - Если
SᵢилиTᵢявляются параметрами типа, пусть иS₀будут их эффективными базовыми классами, в противном случае давайтеT₀S₀T₀SᵢTᵢи соответственно.
- Если
- Найдите набор типов,
Dиз которых будут считаться определяемые пользователем операторы преобразования. Этот набор состоит изS₀(если существует и является классом или структурой), базовыми классамиS₀(еслиS₀S₀он существует и является классом),T₀(если это класс или структура), а также базовые классыT₀(еслиT₀T₀это класс). Тип добавляется в наборD, только если преобразование удостоверений в другой тип, уже включенный в набор, не существует. - Найдите набор применимых пользовательских и снятых операторов преобразования.
UЭтот набор состоит из определяемых пользователем и снятых неявных или явных операторов преобразования, объявленных классами или структурами,Dкоторые преобразуются из типа, охватывающего или охватывающего (если она существует) в тип, охватывающийEилиSTохватываемый. ЕслиUзначение пусто, преобразование не определено и возникает ошибка во время компиляции. - Найдите наиболее конкретный тип источника ,
Sₓоператоров вU:- Если S существует и любой из операторов,
Uпреобразованных изS, тоSₓимеет значениеS. - В противном случае, если любой из операторов
Uпреобразования из типов,Eохватывающих, являетсяSₓнаиболее охватывающимся типом в объединенном наборе исходных типов этих операторов. Если наиболее охватываемый тип не найден, преобразование неоднозначно и возникает ошибка во время компиляции. -
SₓВ противном случае является наиболее охватывающим типом в объединенном наборе типов источников операторов вU. Если не удается найти именно один наиболее охватывающий тип, преобразование неоднозначно и возникает ошибка во время компиляции.
- Если S существует и любой из операторов,
- Найдите наиболее конкретный тип целевого объекта ,
Tₓоператоров вU:- Если любой из операторов преобразования
UTв , тоTₓимеет значениеT. - В противном случае, если любой из операторов
Uпреобразования в типы, охватываемыеT, являетсяTₓнаиболее охватывающим типом в объединенном наборе целевых типов этих операторов. Если не удается найти именно один наиболее охватывающий тип, преобразование неоднозначно и возникает ошибка во время компиляции. -
TₓВ противном случае наиболее охватываемый тип в объединенном наборе целевых типов операторов вU. Если наиболее охватываемый тип не найден, преобразование неоднозначно и возникает ошибка во время компиляции.
- Если любой из операторов преобразования
- Найдите наиболее конкретный оператор преобразования:
- Если U содержит ровно один определяемый пользователем оператор преобразования, который преобразуется из
SₓTₓ, то это наиболее конкретный оператор преобразования. - В противном случае, если
Uсодержится ровно один оператор преобразования, который преобразуется изSₓTₓ, то это наиболее конкретный оператор преобразования. - В противном случае преобразование неоднозначно и возникает ошибка во время компиляции.
- Если U содержит ровно один определяемый пользователем оператор преобразования, который преобразуется из
- Наконец, примените преобразование:
- Если
Eу него еще нет типаSₓ, выполняется стандартное явное преобразование из ESₓ. - Наиболее конкретный определяемый пользователем оператор преобразования вызывается для преобразования из
SₓTₓ. - Если
TₓнетT, то выполняется стандартное явное преобразование изTₓнееT.
- Если
Определяемое пользователем явное преобразование типа в тип ST существует, если определяемое пользователем явное преобразование существует из переменной типа ST в .
10.6 Преобразования с использованием типов, допускающих значение NULL
Преобразования, допускающие значение NULL 10.6.1
Преобразование, допускающее значение NULL , позволяет предварительно определенному преобразованию, которое работает с типом значения, не допускающим значение NULL, также использоваться с формой, допускающей значение NULL этого типа. Для каждого предопределенного неявного или явного преобразования, преобразующегося из типа значения, не допускающего значения NULL, в тип ST не допускающего значения NULL (§10.2.2, §10.2.3, §10.2.4, §10.2.11, §10.3.2 и §10.3.3) существуют следующие преобразования, допускающие значение NULL:
- Неявное или явное преобразование из
S?T? - Неявное или явное преобразование из
ST? - Явное преобразование из
S?T.
Преобразование, допускающее значение NULL, классифицируется как неявное или явное преобразование.
Некоторые преобразования, допускающие значение NULL, классифицируются как стандартные преобразования и могут возникать в рамках определяемого пользователем преобразования. В частности, все неявные преобразования, допускающие значение NULL, классифицируются как стандартные неявные преобразования (§10.4.2), и эти явные преобразования, допускающие значение NULL, которые удовлетворяют требованиям §10.4.3 , классифицируются как стандартные явные преобразования.
Оценка преобразования, допускающего значение NULL, на основе базового преобразования из ST следующего процесса:
- Если преобразование, допускаемое значение NULL, выполняется в
S?T?:- Если исходное значение равно NULL (
HasValueсвойствоfalseравно null), результатом является значение NULL типаT?. - В противном случае преобразование вычисляется как распаку из в , за которым следует базовое преобразование из
S?S, за которым следует оболочка изST.TT?
- Если исходное значение равно NULL (
- Если преобразование,
Sдопускающее значение NULL, выполняется преобразование как базовое преобразование,T?Sза которым следует оболочка изTT.T? - Если преобразование, допускаемое значение
S?NULL, выполняется оценка преобразования как распаку сTS?последующих базовых преобразований вSS.T
10.6.2 Поднятые преобразования
Учитывая определяемый пользователем оператор преобразования, который преобразуется из типа S значения, не допускающего значения NULL, в тип Tзначения, не допускающего значения NULL, существует оператор преобразования , который преобразуется из S?T?. Этот оператор преобразования поднимаемого типа выполняет распаку из-за заданного пользователем преобразования, S?SSTTT? за исключением того, что значение NULL преобразуется непосредственно в значение NULL.S?T? Оператор преобразования лифта имеет ту же неявную или явную классификацию, что и его базовый определяемый пользователем оператор преобразования.
10.7 Анонимные преобразования функций
10.7.1 Общие
Anonymous_method_expression или lambda_expression классифицируется как анонимная функция (§12.21). Выражение не имеет типа, но может быть неявно преобразовано в совместимый тип делегата. Некоторые лямбда-выражения также могут быть неявно преобразованы в совместимый тип дерева выражений.
В частности, анонимная функция F совместима с типом D делегата, предоставленным:
- Если
Fсодержит anonymous_function_signature,Dто иFимеет то же количество параметров. - Если
Fне содержит anonymous_function_signature, тоDможет иметь ноль или более параметров любого типа, если параметрDне является выходным параметром. - Если
Fимеется явно типизированный список параметров, каждый параметр имеетDте же модификаторы, что и соответствующий параметр,Fи преобразование удостоверений существует между соответствующим параметром вF. - Если
Fимеет неявно типизированный список параметров,Dнет ссылочных или выходных параметров. - Если тело
Fпредставляет собой выражение, и либоDимеет тип возвращаемого значения void, илиFявляется асинхронным иDимеет тип возвращаемого значения«TaskType»(§15.14.1), то, когда каждому параметруFприсваивается тип соответствующего параметра вD, телоFстановится допустимым выражением (относительно §12), которое разрешено как statement_expression (§13.7). - Если текст
Fблока иDимеет тип возвращаемого значения void илииFимеетDтип возвращаемого значения, то при указании каждого параметра соответствующего параметра«TaskType»вFтекстеDдопустимого блока (w.r.tF), в котором оператор не указывает выражение. - Если тело
Fявляется выражением, и либоFне является асинхронным иDимеет тип возврата, отличный отvoid,Tявляется асинхронным иFимеет тип возвратаD(«TaskType»<T>), то, когда каждому параметру присваивается тип соответствующего параметра вF, телоDявляется допустимым выражением (в соответствии сF), которое неявно преобразуется в . - Если тело является блоком, а
Fтакже имеет тип несинхронногоFи имеет типDTFиDимеет тип возвращаемого значения, то при указании каждого параметра соответствующего параметра«TaskType»<T>вFтекстеDдопустимого блока инструкций (w.r.tF) с недоступной конечной точкой, в которой каждая инструкция возврата указывает выражение, которое неявно преобразуется в .
Пример. В следующих примерах показаны следующие правила:
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указании типаintx + 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.5) можно использовать в качестве альтернативного синтаксиса для преобразования анонимного метода в тип делегата.
Список вызовов делегата, созданный из анонимной функции, содержит одну запись. Точный целевой объект и целевой метод делегата не определены. В частности, не указано, является 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) в совместимый тип делегата (§21.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. - Методы-кандидаты считаются только теми методами, которые применимы в обычной форме и не пропускают необязательные параметры (§12.6.4.2). Таким образом, методы кандидатов игнорируются, если они применимы только в развернутой форме, или если один или несколько их необязательных параметров не имеют соответствующего параметра в
D.
- Список
- Преобразование считается существующим, если алгоритм §12.8.10.2 создает один лучший метод
M, совместимый с (§21.4).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делегата получается со ссылкой на метод, который был определен во время компиляции, и ссылка на целевой объект, вычисляемый выше, как показано ниже.- Преобразованию разрешено (но не обязательно) использовать существующий экземпляр делегата, который уже содержит эти ссылки.
- Если существующий экземпляр не использовался повторно, создается новый экземпляр (§21.5). Если для выделения нового экземпляра недостаточно памяти,
System.OutOfMemoryExceptionсоздается исключение. В противном случае экземпляр инициализирован с заданными ссылками.
ECMA C# draft specification