Поделиться через


10 преобразований

10.1 Общие

Преобразование приводит к преобразованию выражения в определенный тип или его обработке; в предыдущем случае преобразование может привести к изменению представления. Преобразования могут быть неявными или явными, и это определяет, требуется ли явное приведение.

Пример. Например, преобразование типа в тип неявно, поэтому выражения типа int int long могут неявно рассматриваться как тип.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.10) применяются только к выражениям типа dynamic (§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и t5t6.

пример конца

Все преобразования удостоверений являются симметричными. Если преобразование удостоверений существует из T₁ T₂, преобразование удостоверений существует из T₂ T₁. Два типа — это преобразование удостоверений, когда преобразование удостоверений существует между двумя типами.

В большинстве случаев преобразование удостоверений не влияет на среду выполнения. Однако так как операции с плавающей запятой могут выполняться с более высокой точностью, чем предписано их типом (§8.3.7), назначение их результатов может привести к потере точности, и явные приведения гарантированно снижают точность до того, что предписано типом (§12.9.7).

10.2.3 Неявные числовые преобразования

Неявные числовые преобразования:

  • Отsbyte, до, int, floatlong, doubleили decimal.short
  • longdoubleushortintfloatulongshortuintОт byte , или .decimal
  • Отshort, до int, long, doublefloatили decimal.
  • От , , , longfloatulongdoubleили .decimalushort intuint
  • Отint, до long, doublefloatили decimal.
  • Отuint, до long, ulong, doublefloatили decimal.
  • floatОт long до , doubleили decimal.
  • floatОт ulong до , doubleили decimal.
  • longintuintdoublefloatushortulongОт char , или .decimal
  • С float на double.

Преобразования из , uintlong или ulong float из intили из 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 до любой class_type S T предоставляется S производный от .T
  • От любого class_type до любой interface_type S T, предоставленной S Tреализации.
  • От любого interface_type до любой interface_type S T предоставляется S производный от .T
  • Из array_type с типом Sᵢ элемента в array_type 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) до любого ссылочного типа.
  • От любого reference_type к reference_typeT, если он имеет неявное удостоверение или преобразование ссылок на reference_type T₀ и T₀ имеет преобразование Tудостоверений в .
  • Из любого reference_type в тип T интерфейса или делегата, если он имеет неявное удостоверение или преобразование ссылок на интерфейс или тип T₀ делегата и T₀ является вариативным (§18.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 таким образом, что преобразование бокса из non_nullable_value_type в другой interface_type I₀ и I₀ преобразование Iудостоверения в .
  • От любого non_nullable_value_type к любой interface_type I таким образом, что преобразование бокса из non_nullable_value_type в другой 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

Преобразование бокса подразумевает создание копии прямоугольного значения. Это отличается от преобразования reference_type в типobject, в котором значение продолжает ссылаться на тот же экземпляр и просто считается менее производным типом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 . class Вместо Point этого значение 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, uintushortshortилиulong, если значение constant_expression находится в диапазоне целевого типа.
  • Constant_expression типа можно преобразовать в тип long ulong, если значение constant_expression не является отрицательным.

10.2.12 Неявные преобразования с параметрами типа

Для type_parameterT, который, как известно, является ссылочным типом (§15.2.5), существуют следующие неявные преобразования ссылок (§10.2.8):

  • От T до эффективного базового класса C, от T любого базового класса до любого базового класса Cи от T любого интерфейса, реализованного C.
  • От T interface_type I в Tэффективном наборе интерфейсов и от T любого базового интерфейсаI.
  • От T параметра типа, T указанного в зависимости от U (§15.2.5U).

    Примечание. Поскольку T в пределах области Tдействия тип времени U выполнения всегда будет ссылочным типом, даже если U он не является ссылочным типом во время компиляции. конечная заметка

  • От литерала NULL (§6.4.5.7) до T.

Для type_parameterT, который не является ссылочным типом §15.2.5, в время компиляции рассматриваются следующие преобразования, связанные T с боксом (§10.2.9). Во время выполнения, если T это тип значения, преобразование выполняется как преобразование бокса. Во время выполнения, если T это ссылочный тип, преобразование выполняется как неявное преобразование ссылок или преобразование удостоверений.

  • От T до эффективного базового класса C, от T любого базового класса до любого базового класса Cи от T любого интерфейса, реализованного C.

    Примечание.C Будет одним из типов System.Objectили System.ValueTypeSystem.Enum противном случае T может быть ссылочным типом). конечная заметка

  • От T interface_type I в Tэффективном наборе интерфейсов и от T любого базового интерфейсаI.

Для type_parameterT, не известного как ссылочный тип, существует неявное преобразование из T указанного T параметра типа в зависимости от Uуказанного параметра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 Преобразование выполняется путем создания экземпляра соответствующего 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 недопустимо, так как преобразование не выполняется.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, ulonguintили char.
  • sbyte От byte илиchar.
  • Отshort, до, byte, uintushort, ulongили char.sbyte
  • Отushort, до sbyte, shortbyteили char.
  • От , , , shortuintushortulongили .charint sbytebyte
  • Отuint, до, byte, ushortshort, intили char.sbyte
  • ushortbyteshortulonguintsbyteintОт long , или .char
  • ushortbyteshortlonguintsbyteintОт ulong , или .char
  • sbyteОт char до , byteили short.
  • intcharulongbyteshortlonguintsbyteushortОт float , или .decimal
  • shortuintfloatcharbyteushortulonglongsbyteintОт double , или .decimal
  • shortuintfloatcharbyteushortulonglongsbyteintОт decimal , или .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 или бесконечно, результат преобразования является неопределенным значением целевого типа.
      • В противном случае исходный операнд округляется до нуля до ближайшего целочисленного значения. Если это целочисленное значение находится в диапазоне целевого типа, это значение является результатом преобразования.
      • В противном случае результат преобразования является неопределенным значением целевого типа.
  • Для преобразования из 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, shortuintlongcharulongintdoubleushortfloatили decimal любой enum_type.
  • От любого enum_type floatlongulonguintchardouble bytedecimalsbyteshortushortintдо , или .
  • От любого enum_type до любого другого enum_type.

Явное преобразование перечисления между двумя типами обрабатывается путем обработки всех участвующих enum_type в качестве базового типа этого enum_type, а затем выполнения неявного или явного числового преобразования между результирующей типы.

Пример. Учитывая enum_type E с и базовым типомint, преобразование из нее обрабатывается как явное числовое преобразование byte E (§10.3.2) byteint в , а преобразование из byte E неявного числового преобразования (§10.2.3) в .byte int пример конца

10.3.4 Явные преобразования, допускающие значение NULL

Явные преобразования, допускающие значение NULL, — это преобразования, допускающие значение NULL (§10.6.1), производные от явных и неявных предопределенных преобразований.

10.3.5 Явные преобразования ссылок

Явные преобразования ссылок:

  • Из объекта в любой другой reference_type.
  • От любого class_type до любого class_type S T предоставляется S базовый класс.T
  • От любого class_type до любой interface_type T S предоставляется S не запечатанный и предоставленный S не реализуется.T
  • От любого interface_type до любого class_type TS предоставляется T не запечатанный или предоставленный T Sреализации.
  • От любого interface_type до любой interface_typeS Tпредоставляется S не является производным от.T
  • Из array_type с типом Sᵢ элемента в array_type S T с типом Tᵢэлемента, если все следующие значения имеют значение true:
    • S и T отличаются только в типе элемента. Другими словами, S и T имеют то же количество измерений.
    • Явное преобразование ссылок существует из Sᵢ Tᵢ.
  • От System.Array и интерфейсов, которые он реализует, в любой array_type.
  • Из одномерной array_type System.Collections.Generic.IList<T>S[] System.Collections.Generic.IReadOnlyList<T> в , а также его базовые интерфейсы, при условии, что имеется преобразование удостоверений или явное преобразование ссылок в .S T
  • От System.Collections.Generic.IList<S>, System.Collections.Generic.IReadOnlyList<S>и их базовых интерфейсов к одномерным типу T[]массива, при условии, что имеется преобразование удостоверений или явное преобразование ссылок из S T.
  • От System.Delegate и интерфейсов, которые он реализует в любой delegate_type.
  • Из ссылочного типа в ссылочный типT, если он имеет явное преобразование ссылок из S ссылочного типа в ссылочный тип T₀ и T₀ имеется преобразование удостоверений в T₀ T.S
  • От ссылочного типа S к интерфейсу или типу T делегата, если он имеет явное преобразование ссылок из S интерфейса или делегатаT₀, и либо T₀ является вариативным или преобразуемым T T₀ T в §18.2.3.3.
  • От D<S₁...Sᵥ> места, где D<X₁...Xᵥ> является универсальным типом делегата, D<S₁...Sᵥ> несовместим с или идентичным D<T₁...Tᵥ>и для каждого параметра Xᵢ типа из следующих удержанийD:D<T₁...Tᵥ>
    • Если 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типа и инициализации каждого из его полей в порядке слева направо путем вычисления соответствующего выражения Eэлемента кортежа, преобразования его в соответствующий тип T элемента с использованием явно найденного преобразования и инициализации поля с результатом.System.ValueTuple<...>

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 до любого non_nullable_value_typeI, где происходит распаковка преобразования из interface_type в тип non_nullable_value I₀ и преобразование удостоверений в .I I₀
  • От любого interface_type до любой non_nullable_value_typeI, в которой происходит распаковка преобразования из interface_type в non_nullable_value_type I₀ и либо I₀ variance_convertible в I или I является вариативным I₀ преобразованием (§18.2.3.3.3).
  • От любого reference_type до любого nullable_value_type, где происходит распаковка преобразования из reference_type в базовый non_nullable_value_type nullable_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. Если исходный System.NullReferenceException операнд вызываетсяnull. Если исходный операнд является ссылкой на несовместимый объект, System.InvalidCastException создается исключение.

Для отмены преобразования в заданный nullable_value_type для успешного выполнения значение исходного операнда должно иметь значение NULL или ссылку на поле базового non_nullable_value_type nullable_value_type. Если исходный операнд является ссылкой на несовместимый объект, System.InvalidCastException создается исключение.

10.3.8 Явные преобразования с параметрами типа

Для type_parameterT, который, как известно, является ссылочным типом (§15.2.5), существуют следующие явные преобразования ссылок (§10.3.5):

  • От эффективного T базового класса C к T любому базовому классу и от любого базового класса C Tдо .
  • От любого interface_type до T.
  • От T любого interface_typeI, предоставленного, еще не выполняется неявное преобразование ссылок в T I.
  • От type_parameterU, T которое T зависит от U (§15.2.5).

    Примечание. Поскольку T в пределах области Tдействия тип времени выполнения всегда будет ссылочным типом, даже если U он не является ссылочным типом во время компиляции. конечная заметка

Для type_parameterT, который не является ссылочным типом (§15.2.5), следующие преобразования, связанные T с компиляцией, считаются распаковкой преобразований (§10.3.7). Во время выполнения, если T это тип значения, преобразование выполняется в виде преобразования распаковки. Во время выполнения, если T это ссылочный тип, преобразование выполняется как явное преобразование ссылок или преобразование удостоверений.

  • От эффективного T базового класса C к T любому базовому классу и от любого базового класса C Tдо .

    Примечание. C будет одним из типов System.Object, System.ValueTypeили (в System.Enum противном случае T может быть ссылочным типом). конечная заметка

  • От любого interface_type до T.

Для type_parameterT, который не известен как ссылочный тип (§15.2.5), существуют следующие явные преобразования:

  • От T любого interface_typeI, предоставленного, еще не выполняется неявное преобразование из T I. Это преобразование состоит из неявного преобразования бокса (§10.2.9), object T за которым следует явное преобразование ссылок в 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
    }
}

Если было разрешено прямое преобразование long t в него, можно легко ожидать, что 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₀ являются разными типами.
  • T₀ Либо S₀ класс или тип структуры, в котором происходит объявление оператора.
  • Ни S₀ T₀ interface_type.
  • За исключением определяемых пользователем преобразований преобразование не существует из или из S T T S.

Ограничения, применяемые к определяемым пользователем преобразованиям, указаны в разделе 15.10.4.

10.5.3 Оценка определяемых пользователем преобразований

Определяемое пользователем преобразование преобразует исходное выражение, которое может иметь исходный тип, в другой тип, называемый целевым типом. Оценка определяемых пользователем центров преобразования при поиске наиболее конкретного определяемого пользователем оператора преобразования для исходного выражения и целевого типа. Это определение разбито на несколько шагов:

  • Поиск набора классов и структур, из которых будут рассматриваться определяемые пользователем операторы преобразования. Этот набор состоит из исходного типа и его базовых классов, если исходный тип существует, а также целевой тип и его базовые классы. Для этого предполагается, что только классы и структуры могут объявлять определяемые пользователем операторы, и что неклассовые типы не имеют базовых классов. Кроме того, если исходный или целевой тип является типом, допускаемым значением NULL, вместо него используется базовый тип.
  • Из этого набора типов, определяющих, какие пользовательские и снятые операторы преобразования применимы. Для применимого оператора преобразования можно выполнить стандартное преобразование (§10.4) из исходного выражения в тип операнда оператора, и можно выполнить стандартное преобразование из типа результата оператора в целевой тип.
  • Из набора применимых пользовательских операторов, определяющих, какой оператор однозначно является наиболее конкретным. Как правило, наиболее конкретный оператор является оператором, тип операнда которого является "ближайшим" к исходному выражению и тип результата которого является "ближайшим" к целевому типу. Определяемые пользователем операторы преобразования предпочтительнее, чем операторы преобразования, поднятые. Точные правила для установления наиболее конкретного определяемого пользователем оператора преобразования определяются в следующих подклаузах.

После определения наиболее конкретного пользовательского оператора преобразования фактическое выполнение определяемого пользователем преобразования включает до трех шагов:

  • Во-первых, при необходимости выполняется стандартное преобразование из исходного выражения в тип операнда определяемого пользователем или оператора преобразования.
  • Затем вызов определяемого пользователем или вызываемого оператора преобразования для выполнения преобразования.
  • Наконец, при необходимости выполняется стандартное преобразование из типа результата определяемого пользователем оператора преобразования в целевой тип.

Оценка определяемого пользователем преобразования никогда не включает несколько определяемых пользователем или поднятых операторов преобразования. Другими словами, преобразование типа в тип S никогда не будет сначала выполнять определяемое пользователем преобразование из X S и затем выполнять определяемое пользователем преобразование из TX .T

  • Точные определения оценки определяемых пользователем неявных или явных преобразований приведены в следующих подклаузах. Определения используют следующие термины:
  • Если стандартное неявное преобразование (§10.4.2) существует от типа к типуBA, и если ни interface_type s A B, то, как сообщается, охватывается иB, как говорятA, A охватывается. B
  • Если стандартное неявное преобразование (§10.4.2) существует из выражения E в типB, и если ни B тип E (если он имеет один) interface_types, тоE, как сообщается, охватывается и, как сообщаетсяE, охватывается.BB
  • Наиболее охватывающий тип в наборе типов является один тип, охватывающий все остальные типы в наборе. Если один тип не охватывает все остальные типы, набор не имеет наиболее охватывающего типа. Более интуитивно понятным является наиболее охватывающий тип в наборе — один тип, в который можно неявно преобразовать друг друга.
  • Наиболее охватываемый тип в наборе типов — это один тип, охватываемый всеми остальными типами в наборе. Если ни один тип не охватывается всеми другими типами, то набор не имеет наиболее охватываемого типа. Более интуитивно понятным типом является самый маленький тип в наборе— один тип, который может быть неявно преобразован в каждый из остальных типов.

10.5.4 Определяемые пользователем неявные преобразования

Определяемое пользователем неявное преобразование из выражения E в тип T обрабатывается следующим образом:

  • Определите типы Sи S₀ T₀.

    • Если E имеет тип, пусть S он будет таким.
    • Если S или T имеют типы значений, допускающие значение NULL, пусть и Tᵢ являются их базовыми типами, в противном случае давайте TSᵢ Sᵢ Tᵢ S и соответствующим образом.
    • Если Sᵢ или Tᵢ являются параметрами типа, пусть и T₀ будут их эффективными базовыми классами, в противном случае давайте TᵢS₀ S₀ T₀ Sₓ и соответственно.
  • Найдите набор типов, 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 содержит ровно один определяемый пользователем оператор преобразования, который преобразуется изTₓSₓ, то это наиболее конкретный оператор преобразования.
    • В противном случае, если U содержится ровно один оператор преобразования, который преобразуется из Sₓ Tₓ, то это наиболее конкретный оператор преобразования.
    • В противном случае преобразование неоднозначно и возникает ошибка во время компиляции.
  • Наконец, примените преобразование:

    • Если тип E еще не имеет Sₓ, то выполняется стандартное неявное преобразование из E Sₓ него.
    • Наиболее конкретный оператор преобразования вызывается для преобразования из Sₓ Tₓ.
    • Если Tₓ нет T, то выполняется стандартное неявное преобразование из Tₓ T неявного.

Определяемое пользователем неявное преобразование типа в тип S T существует, если определяемое пользователем неявное преобразование существует из переменной типа S Tв .

10.5.5 Определяемые пользователем явные преобразования

Определяемое пользователем явное преобразование из выражения E в тип T обрабатывается следующим образом:

  • Определите типы Sи S₀ T₀.
    • Если E имеет тип, пусть S он будет таким.
    • Если S или T имеют типы значений, допускающие значение NULL, пусть и Tᵢ являются их базовыми типами, в противном случае давайте TSᵢ Sᵢ Tᵢ S и соответствующим образом.
    • Если Sᵢ или Tᵢ являются параметрами типа, пусть и T₀ будут их эффективными базовыми классами, в противном случае давайте TᵢS₀ S₀ T₀ Sᵢ и соответственно.
  • Найдите набор типов, 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. Если не удается найти именно один наиболее охватывающий тип, преобразование неоднозначно и возникает ошибка во время компиляции.
  • Найдите наиболее конкретный тип целевого объекта , Tₓоператоров в U:
    • Если любой из операторов преобразования U Tв , то Tₓ имеет значение T.
    • В противном случае, если любой из операторов U преобразования в типы, охватываемые T, является Tₓ наиболее охватывающим типом в объединенном наборе целевых типов этих операторов. Если не удается найти именно один наиболее охватывающий тип, преобразование неоднозначно и возникает ошибка во время компиляции.
    • Tₓ В противном случае наиболее охватываемый тип в объединенном наборе целевых типов операторов в U. Если наиболее охватываемый тип не найден, преобразование неоднозначно и возникает ошибка во время компиляции.
  • Найдите наиболее конкретный оператор преобразования:
    • Если U содержит ровно один определяемый пользователем оператор преобразования, который преобразуется из Sₓ Tₓ, то это наиболее конкретный оператор преобразования.
    • В противном случае, если U содержится ровно один оператор преобразования, который преобразуется из Sₓ Tₓ, то это наиболее конкретный оператор преобразования.
    • В противном случае преобразование неоднозначно и возникает ошибка во время компиляции.
  • Наконец, примените преобразование:
    • Если E у него еще нет типа Sₓ, выполняется стандартное явное преобразование из E Sₓ .
    • Наиболее конкретный определяемый пользователем оператор преобразования вызывается для преобразования из Sₓ Tₓ.
    • Если Tₓ нет T, то выполняется стандартное явное преобразование из Tₓ нее T .

Определяемое пользователем явное преобразование типа в тип S T существует, если определяемое пользователем явное преобразование существует из переменной типа TS в .

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?
  • Неявное или явное преобразование из ST?
  • Явное преобразование из 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 T, за которым следует оболочка из T?T .S? S
  • Если преобразование, S допускающее значение NULL, выполняется преобразование как базовое преобразование, T S за которым следует оболочка из T T?.T?
  • Если преобразование, допускаемое значение S? NULL, выполняется оценка преобразования как распаку с S S? последующих базовых преобразований в TS .T

10.6.2 Поднятые преобразования

Учитывая определяемый пользователем оператор преобразования, который преобразуется из типа S значения, не допускающего значения NULL, в тип Tзначения, не допускающего значения NULL, существует оператор преобразования , который преобразуется из S? T?. Этот оператор преобразования поднимаемого типа выполняет распаку из-за заданного пользователем преобразования, S? T T S T?S за исключением того, что значение 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 илиF асинхронный и D имеет «TaskType» тип возвращаемого значения (§15.15.1), то при указании каждого параметра соответствующего параметра F в Dтексте F допустимого выражения (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 имеет асинхронный D «TaskType»<T> типF Tи имеетvoid тип возвращаемого значения (§15.15.1), то при указании каждого параметра соответствующего параметра F в Dтексте F допустимого выражения (w.r.t §12), которое неявно преобразуется Tв .
  • Если тело является блоком, аF также имеет тип несинхронного F и имеет тип Tвозвращаемого значения, асинхронный«TaskType»<T> DF и D имеет тип возвращаемого значения, то при указании каждого параметра соответствующего параметра 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 указании типа 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.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) соответствующего параметра в parameter_list D , за исключением параметров типаdynamic, где соответствующее выражение имеет тип object вместо dynamic.
    • Методы-кандидаты считаются только теми методами, которые применимы в обычной форме и не пропускают необязательные параметры (§12.6.4.2). Таким образом, методы кандидатов игнорируются, если они применимы только в развернутой форме, или если один или несколько их необязательных параметров не имеют соответствующего параметра в D.
  • Преобразование считается существующим, если алгоритм §12.8.10.2 создает один лучший методM, совместимый с (§20.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 делегата получается со ссылкой на метод, который был определен во время компиляции, и ссылка на целевой объект, вычисляемый выше, как показано ниже.
    • Преобразованию разрешено (но не обязательно) использовать существующий экземпляр делегата, который уже содержит эти ссылки.
    • Если существующий экземпляр не использовался повторно, создается новый экземпляр (§20.5). Если для выделения нового экземпляра недостаточно памяти, System.OutOfMemoryException создается исключение. В противном случае экземпляр инициализирован с заданными ссылками.