转换Conversions

*转换 _ 允许将表达式视为特定类型。A *conversion _ enables an expression to be treated as being of a particular type. 转换可能会导致将给定类型的表达式视为具有不同的类型,也可能导致不具有类型的表达式获得类型。A conversion may cause an expression of a given type to be treated as having a different type, or it may cause an expression without a type to get a type. 转换可以是 *隐式* 或 _ explicit *,这确定是否需要显式强制转换。Conversions can be implicit or _*explicit**, and this determines whether an explicit cast is required. 例如,从类型 int 到类型的转换 long 是隐式的,因此类型的表达式 int 可以隐式对待为类型 longFor instance, the conversion from type int to type long is implicit, so expressions of type int can implicitly be treated as type long. 相反,从类型 long 到类型的转换 int 是显式的,因此需要显式强制转换。The opposite conversion, from type long to type int, is explicit and so an explicit cast is required.

int a = 123;
long b = a;         // implicit conversion from int to long
int c = (int) b;    // explicit conversion from long to int

某些转换是由语言定义的。Some conversions are defined by the language. 程序还可以定义自己的转换 (用户定义的转换) 。Programs may also define their own conversions (User-defined conversions).

隐式转换Implicit conversions

以下转换归类为隐式转换:The following conversions are classified as implicit conversions:

  • 标识转换Identity conversions
  • 隐式数值转换Implicit numeric conversions
  • 隐式枚举转换Implicit enumeration conversions
  • 隐式内插字符串转换Implicit interpolated string conversions
  • 隐式可为空转换Implicit nullable conversions
  • Null 文本转换Null literal conversions
  • 隐式引用转换Implicit reference conversions
  • 装箱转换Boxing conversions
  • 隐式动态转换Implicit dynamic conversions
  • 隐式常量表达式转换Implicit constant expression conversions
  • 用户定义的隐式转换User-defined implicit conversions
  • 匿名函数转换Anonymous function conversions
  • 方法组转换Method group conversions

隐式转换可能会在多种情况下发生,包括函数成员调用 (编译时检查动态重载解析) 、强制转换表达式 (强制转换表达式) ,并 (赋值运算符) 赋值运算符Implicit conversions can occur in a variety of situations, including function member invocations (Compile-time checking of dynamic overload resolution), cast expressions (Cast expressions), and assignments (Assignment operators).

预定义的隐式转换始终会成功,并且永远不会引发异常。The pre-defined implicit conversions always succeed and never cause exceptions to be thrown. 正确设计的用户定义隐式转换也应显示这些特征。Properly designed user-defined implicit conversions should exhibit these characteristics as well.

出于转换目的,类型 object 和被 dynamic 视为等效的。For the purposes of conversion, the types object and dynamic are considered equivalent.

但是,动态转换 (隐式动态转换显式动态转换) 仅应用于 dynamic (动态类型) 的类型的表达式。However, dynamic conversions (Implicit dynamic conversions and Explicit dynamic conversions) apply only to expressions of type dynamic (The dynamic type).

标识转换Identity conversion

标识转换从任何类型转换为同一类型。An identity conversion converts from any type to the same type. 此转换存在,因此,已具有所需类型的实体可被视为可转换为该类型。This conversion exists such that an entity that already has a required type can be said to be convertible to that type.

  • 因为和被视为等效的,所以在 object dynamic 和之间存在一个标识转换, object dynamic 在将的所有匹配项替换为相同的构造类型之间 dynamic objectBecause object and dynamic are considered equivalent there is an identity conversion between object and dynamic, and between constructed types that are the same when replacing all occurrences of dynamic with object.

隐式数值转换Implicit numeric conversions

隐式数值转换为:The implicit numeric conversions are:

  • sbyte 到、、、、 short int long float doubledecimalFrom sbyte to short, int, long, float, double, or decimal.
  • 从到、、、、、、、 byte short ushort int uint long ulong float doubledecimalFrom byte to short, ushort, int, uint, long, ulong, float, double, or decimal.
  • shortint 、、、 long float doubledecimalFrom short to int, long, float, double, or decimal.
  • 从到、、、、、 ushort int uint long ulong float doubledecimalFrom ushort to int, uint, long, ulong, float, double, or decimal.
  • intlongfloatdoubledecimalFrom int to long, float, double, or decimal.
  • uintlong 、、、 ulong float doubledecimalFrom uint to long, ulong, float, double, or decimal.
  • longfloatdoubledecimalFrom long to float, double, or decimal.
  • ulongfloatdoubledecimalFrom ulong to float, double, or decimal.
  • 从到、、、、、、 char ushort int uint long ulong float doubledecimalFrom char to ushort, int, uint, long, ulong, float, double, or decimal.
  • floatdoubleFrom float to double.

int 、、 uint long 或到之间的转换 ulong float long ulong double 可能会导致精度损失,但永远不会导致数量级损失。Conversions from int, uint, long, or ulong to float and from long or ulong to double may cause a loss of precision, but will never cause a loss of magnitude. 其他隐式数值转换不会丢失任何信息。The other implicit numeric conversions never lose any information.

不存在对类型的隐式转换 char ,因此其他整型类型的值不会自动转换为 char 类型。There are no implicit conversions to the char type, so values of the other integral types do not automatically convert to the char type.

隐式枚举转换Implicit enumeration conversions

隐式枚举转换允许 decimal_integer_literal 0 转换为任何 enum_type ,以及基础类型为 enum_type 的任何 nullable_typeAn implicit enumeration conversion permits the decimal_integer_literal 0 to be converted to any enum_type and to any nullable_type whose underlying type is an enum_type. 在后一种情况下,转换将通过转换为基础 enum_type 进行计算,并将结果包装 (可以为 null 的类型) 。In the latter case the conversion is evaluated by converting to the underlying enum_type and wrapping the result (Nullable types).

隐式内插字符串转换Implicit interpolated string conversions

隐式内插字符串转换允许 interpolated_string_expression (内 插字符串) 转换为 System.IFormattable System.FormattableString 实现) 的或 (System.IFormattableAn implicit interpolated string conversion permits an interpolated_string_expression (Interpolated strings) to be converted to System.IFormattable or System.FormattableString (which implements System.IFormattable).

应用此转换时,字符串值不是由内插字符串组成的。When this conversion is applied a string value is not composed from the interpolated string. 相反 System.FormattableString ,将创建的实例,如内 插字符串中所述。Instead an instance of System.FormattableString is created, as further described in Interpolated strings.

隐式可为空转换Implicit nullable conversions

对不可以为 null 的值类型进行操作的预定义隐式转换也可用于这些类型的可以为 null 的形式。Predefined implicit conversions that operate on non-nullable value types can also be used with nullable forms of those types. 对于从不可为 null 的值类型转换为不可为 null 的值类型的每个预定义隐式标识和数值转换 S T ,存在以下隐式可为 null 的转换:For each of the predefined implicit identity and numeric conversions that convert from a non-nullable value type S to a non-nullable value type T, the following implicit nullable conversions exist:

  • 从到的隐式转换 S? T?An implicit conversion from S? to T?.
  • 从到的隐式转换 S T?An implicit conversion from S to T?.

根据中的基础转换,计算隐式可为 null 的转换, S T 如下所示:Evaluation of an implicit nullable conversion based on an underlying conversion from S to T proceeds as follows:

  • 如果可以为 null 的转换从 S?T?If the nullable conversion is from S? to T?:

    • 如果源值为 null (HasValue 属性为 false) ,则结果为类型的 null 值 T?If the source value is null (HasValue property is false), the result is the null value of type T?.
    • 否则,转换将作为从到的解包进行计算 S? S ,后跟从到的基础转换 S T ,然后) 从到的包装 (可以为 null 的类型 T T?Otherwise, the conversion is evaluated as an unwrapping from S? to S, followed by the underlying conversion from S to T, followed by a wrapping (Nullable types) from T to T?.
  • 如果可以为 null 的转换从 ST? ,则会将转换计算为从到的基础转换, S 并将从到 T 的换行 T T?If the nullable conversion is from S to T?, the conversion is evaluated as the underlying conversion from S to T followed by a wrapping from T to T?.

Null 文本转换Null literal conversions

存在从 null 文本到任何可以为 null 的类型的隐式转换。An implicit conversion exists from the null literal to any nullable type. 这种转换会生成 null 值, (给定可以为 null 的类型) 为 null 的类型。This conversion produces the null value (Nullable types) of the given nullable type.

隐式引用转换Implicit reference conversions

隐式引用转换为:The implicit reference conversions are:

  • 从任何 reference_typeobjectdynamicFrom any reference_type to object and dynamic.
  • 从任何 class_type S 到任何 class_type TS 都是从派生的 TFrom any class_type S to any class_type T, provided S is derived from T.
  • 从任何 class_type S 到任何 interface_type T ,提供的 S 实现 TFrom any class_type S to any interface_type T, provided S implements T.
  • 从任何 interface_type S 到任何 interface_type TS 都是从派生的 TFrom any interface_type S to any interface_type T, provided S is derived from T.
  • 在 array_type 具有元素类型的 array_typeS ,如果 SE T TE 满足以下所有条件:From an array_type S with an element type SE to an array_type T with an element type TE, provided all of the following are true:
    • ST 仅因元素类型而异。S and T differ only in element type. 换言之, ST 具有相同的维数。In other words, S and T have the same number of dimensions.
    • SETE 都是 reference_typeBoth SE and TE are reference_type s.
    • 存在从到的隐式引用转换 SE TEAn implicit reference conversion exists from SE to TE.
  • 从任何 array_type System.Array 和它实现的接口。From any array_type to System.Array and the interfaces it implements.
  • 从一维数组类型 S[]System.Collections.Generic.IList<T> 及其基接口,前提是存在隐式标识或从到的引用转换 S TFrom a single-dimensional array type S[] to System.Collections.Generic.IList<T> and its base interfaces, provided that there is an implicit identity or reference conversion from S to T.
  • 从任何 delegate_type System.Delegate 和它实现的接口。From any delegate_type to System.Delegate and the interfaces it implements.
  • 从 null 文本到任何 reference_typeFrom the null literal to any reference_type.
  • 从任何 reference_typereference_typeT 如果它具有隐式标识或到 reference_type 的引用转换 T0 ,并且 T0 具有到的标识转换 TFrom any reference_type to a reference_type T if it has an implicit identity or reference conversion to a reference_type T0 and T0 has an identity conversion to T.
  • 从任何 reference_type 到接口或委托类型( T 如果它具有隐式标识或到接口或委托类型的引用转换 T0 ),并且) 到之间存在可转换的 T0 (方差转换 TFrom any reference_type to an interface or delegate type T if it has an implicit identity or reference conversion to an interface or delegate type T0 and T0 is variance-convertible (Variance conversion) to T.
  • 涉及称为引用类型的类型参数的隐式转换。Implicit conversions involving type parameters that are known to be reference types. 有关涉及类型参数的隐式转换的更多详细信息,请参阅 涉及类型参数的隐式转换See Implicit conversions involving type parameters for more details on implicit conversions involving type parameters.

隐式引用转换是 reference_type 之间的转换,这些转换可证明始终成功,因此不需要在运行时进行检查。The implicit reference conversions are those conversions between reference_type s that can be proven to always succeed, and therefore require no checks at run-time.

引用转换、隐式或显式转换决不会更改正在转换的对象的引用标识。Reference conversions, implicit or explicit, never change the referential identity of the object being converted. 换言之,虽然引用转换可以更改引用的类型,但它不会更改引用的对象的类型或值。In other words, while a reference conversion may change the type of the reference, it never changes the type or value of the object being referred to.

装箱转换Boxing conversions

装箱转换允许 value_type 隐式转换为引用类型。A boxing conversion permits a value_type to be implicitly converted to a reference type. object dynamic System.ValueType non_nullable_value_type 实现的任何 interface_type ,都存在从到、、以及的任何 non_nullable_value_type 的装箱转换。A boxing conversion exists from any non_nullable_value_type to object and dynamic, to System.ValueType and to any interface_type implemented by the non_nullable_value_type. 此外,可以将 enum_type 转换为类型 System.EnumFurthermore an enum_type can be converted to the type System.Enum.

如果且仅当存在从基础 non_nullable_value_type 到引用类型的装箱转换,则从 nullable_type 到引用类型的装箱转换。A boxing conversion exists from a nullable_type to a reference type, if and only if a boxing conversion exists from the underlying non_nullable_value_type to the reference type.

如果值类型 I 具有到接口类型的装箱转换 I0 ,并且其 I0 标识转换为,则该值类型具有到接口类型的装箱转换 IA value type has a boxing conversion to an interface type I if it has a boxing conversion to an interface type I0 and I0 has an identity conversion to I.

如果值类型 I 具有到接口类型的装箱转换或委托类型的装箱转换 I0 并且 I0 (方差转换) 为变体,则该值类型具有到的装箱转换 IA value type has a boxing conversion to an interface type I if it has a boxing conversion to an interface or delegate type I0 and I0 is variance-convertible (Variance conversion) to I.

non_nullable_value_type 的值装箱包括分配对象实例并将 value_type 值复制到该实例中。Boxing a value of a non_nullable_value_type consists of allocating an object instance and copying the value_type value into that instance. 结构可以装箱到类型 System.ValueType ,因为这是所有结构的基类 (继承) 。A struct can be boxed to the type System.ValueType, since that is a base class for all structs (Inheritance).

nullable_type 的值装箱将继续执行以下操作:Boxing a value of a nullable_type proceeds as follows:

  • 如果源值为 null (HasValue 属性为 false) ,则结果将为目标类型的空引用。If the source value is null (HasValue property is false), the result is a null reference of the target type.
  • 否则,结果将是对 T 源值解包和装箱而生成的装箱的引用。Otherwise, the result is a reference to a boxed T produced by unwrapping and boxing the source value.

装箱转换中进一步介绍了装箱转换。Boxing conversions are described further in Boxing conversions.

隐式动态转换Implicit dynamic conversions

存在从类型为的表达式到任何类型的隐式动态转换 dynamic TAn implicit dynamic conversion exists from an expression of type dynamic to any type T. 转换是动态绑定 (动态绑定) ,这意味着将在运行时从表达式的运行时类型中查找隐式转换 TThe conversion is dynamically bound (Dynamic binding), which means that an implicit conversion will be sought at run-time from the run-time type of the expression to T. 如果未找到任何转换,则会引发运行时异常。If no conversion is found, a run-time exception is thrown.

请注意,此隐式转换似乎违反了隐式转换开始时的 建议,隐 式转换应永远不会引发异常。Note that this implicit conversion seemingly violates the advice in the beginning of Implicit conversions that an implicit conversion should never cause an exception. 但它不是转换本身,而是 查找 导致异常的转换。However it is not the conversion itself, but the finding of the conversion that causes the exception. 运行时异常的风险在使用动态绑定时是固有的。The risk of run-time exceptions is inherent in the use of dynamic binding. 如果不需要转换的动态绑定,则可以先将该表达式转换为 object ,然后再转换为所需的类型。If dynamic binding of the conversion is not desired, the expression can be first converted to object, and then to the desired type.

下面的示例阐释了隐式动态转换:The following example illustrates implicit dynamic conversions:

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 都采用隐式动态转换,在这种情况下,将在运行时暂停操作的绑定。The assignments to s2 and i both employ implicit dynamic conversions, where the binding of the operations is suspended until run-time. 在运行时,隐式转换从的运行时类型中查找 d -- string 到目标类型。At run-time, implicit conversions are sought from the run-time type of d -- string -- to the target type. 找到到, string 但不能转换为 intA conversion is found to string but not to int.

隐式常量表达式转换Implicit constant expression conversions

隐式常数表达式转换允许以下转换:An implicit constant expression conversion permits the following conversions:

  • int sbyte byte short ushort uint ulong 如果 constant_expression 的值在目标类型的范围内,则可以将类型的 constant_expression (常数表达式) 转换为类型、、、、或。A constant_expression (Constant expressions) of type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant_expression is within the range of the destination type.
  • long ulong 如果 constant_expression 的值不为负数,则可以将类型的 constant_expression 转换为类型。A constant_expression of type long can be converted to type ulong, provided the value of the constant_expression is not negative.

涉及类型参数的隐式转换Implicit conversions involving type parameters

给定的类型参数存在以下隐式转换 TThe following implicit conversions exist for a given type parameter T:

  • 从到 T 其有效的基类 C ,从到的任何基类,从到所 T C T 实现的任何接口 CFrom T to its effective base class C, from T to any base class of C, and from T to any interface implemented by C. 在运行时,如果 T 是值类型,则转换将作为装箱转换执行。At run-time, if T is a value type, the conversion is executed as a boxing conversion. 否则,转换将作为隐式引用转换或标识转换执行。Otherwise, the conversion is executed as an implicit reference conversion or identity conversion.
  • T I T 设置为有效接口中的接口类型,并从设置 T 为的任何基接口 IFrom T to an interface type I in T's effective interface set and from T to any base interface of I. 在运行时,如果 T 是值类型,则转换将作为装箱转换执行。At run-time, if T is a value type, the conversion is executed as a boxing conversion. 否则,转换将作为隐式引用转换或标识转换执行。Otherwise, the conversion is executed as an implicit reference conversion or identity conversion.
  • T 到类型参数 UT 具体取决于 U) (类型参数约束From T to a type parameter U, provided T depends on U (Type parameter constraints). 在运行时,如果 U 是值类型,则和的 T U 类型必须相同,并且不执行任何转换。At run-time, if U is a value type, then T and U are necessarily the same type and no conversion is performed. 否则,如果 T 是值类型,则转换将作为装箱转换执行。Otherwise, if T is a value type, the conversion is executed as a boxing conversion. 否则,转换将作为隐式引用转换或标识转换执行。Otherwise, the conversion is executed as an implicit reference conversion or identity conversion.
  • 从 null 文本到 T ,已知为 T 引用类型。From the null literal to T, provided T is known to be a reference type.
  • T向引用类型( I 如果它具有到引用类型的隐式转换 S0 和到的 S0 标识转换) SFrom T to a reference type I if it has an implicit conversion to a reference type S0 and S0 has an identity conversion to S. 在运行时,转换的执行方式与转换到的方式相同 S0At run-time the conversion is executed the same way as the conversion to S0.
  • T 到接口类型 I (如果它具有到接口或委托类型的隐式转换 I0 )并且可以转换为 I0 I (方差转换) 。From T to an interface type I if it has an implicit conversion to an interface or delegate type I0 and I0 is variance-convertible to I (Variance conversion). 在运行时,如果 T 是值类型,则转换将作为装箱转换执行。At run-time, if T is a value type, the conversion is executed as a boxing conversion. 否则,转换将作为隐式引用转换或标识转换执行。Otherwise, the conversion is executed as an implicit reference conversion or identity conversion.

如果 T 已知 (类型参数约束) 为引用类型,则上述转换全都归类为隐式引用转换 (隐式引用转换) 。If T is known to be a reference type (Type parameter constraints), the conversions above are all classified as implicit reference conversions (Implicit reference conversions). 如果 T 不知道是引用类型,上面的转换归类为装箱转换 (装箱转换) 。If T is not known to be a reference type, the conversions above are classified as boxing conversions (Boxing conversions).

用户定义的隐式转换User-defined implicit conversions

用户定义的隐式转换包括一个可选的标准隐式转换,然后执行用户定义的隐式转换运算符,然后执行另一个可选的标准隐式转换。A user-defined implicit conversion consists of an optional standard implicit conversion, followed by execution of a user-defined implicit conversion operator, followed by another optional standard implicit conversion. 用于评估用户定义的隐式转换的确切规则在 处理用户定义的隐式转换中进行了介绍。The exact rules for evaluating user-defined implicit conversions are described in Processing of user-defined implicit conversions.

匿名函数转换和方法组转换Anonymous function conversions and method group conversions

匿名函数和方法组本身没有类型,但可能会隐式转换为委托类型或表达式树类型。Anonymous functions and method groups do not have types in and of themselves, but may be implicitly converted to delegate types or expression tree types. 匿名函数转换和方法组转换中的方法组转换更详细地介绍了匿名函数转换。Anonymous function conversions are described in more detail in Anonymous function conversions and method group conversions in Method group conversions.

显式转换Explicit conversions

以下转换归类为显式转换:The following conversions are classified as explicit conversions:

  • 所有隐式转换。All implicit conversions.
  • 显式数值转换。Explicit numeric conversions.
  • 显式枚举转换。Explicit enumeration conversions.
  • 可以为 null 的显式转换。Explicit nullable conversions.
  • 显式引用转换。Explicit reference conversions.
  • 显式接口转换。Explicit interface conversions.
  • 取消装箱转换。Unboxing conversions.
  • 显式动态转换Explicit dynamic conversions
  • 用户定义的显式转换。User-defined explicit conversions.

显式转换可在转换表达式 () 转换表达式 中发生。Explicit conversions can occur in cast expressions (Cast expressions).

显式转换集包括所有隐式转换。The set of explicit conversions includes all implicit conversions. 这意味着允许冗余强制转换表达式。This means that redundant cast expressions are allowed.

不是隐式转换的显式转换是转换,这种转换不能始终成功、已知的转换可能会丢失信息,并且跨类型的域转换的类型充分不同,以确保显式表示法。The explicit conversions that are not implicit conversions are conversions that cannot be proven to always succeed, conversions that are known to possibly lose information, and conversions across domains of types sufficiently different to merit explicit notation.

显式数值转换Explicit numeric conversions

显式数值转换是指从 numeric_type 到另一个 numeric_type 的转换, (隐式 数值转换) 尚不存在:The explicit numeric conversions are the conversions from a numeric_type to another numeric_type for which an implicit numeric conversion (Implicit numeric conversions) does not already exist:

  • sbytebyte 、、、 ushort uint ulongcharFrom sbyte to byte, ushort, uint, ulong, or char.
  • bytesbytecharFrom byte to sbyte and char.
  • short 到、、、、 sbyte byte ushort uint ulongcharFrom short to sbyte, byte, ushort, uint, ulong, or char.
  • ushortsbytebyteshortcharFrom ushort to sbyte, byte, short, or char.
  • 从到、、、、、 int sbyte byte short ushort uint ulongcharFrom int to sbyte, byte, short, ushort, uint, ulong, or char.
  • uint 到、、、、 sbyte byte short ushort intcharFrom uint to sbyte, byte, short, ushort, int, or char.
  • 从到、、、、、、 long sbyte byte short ushort int uint ulongcharFrom long to sbyte, byte, short, ushort, int, uint, ulong, or char.
  • 从到、、、、、、 ulong sbyte byte short ushort int uint longcharFrom ulong to sbyte, byte, short, ushort, int, uint, long, or char.
  • charsbytebyteshortFrom char to sbyte, byte, or short.
  • 从到、、、、、、、、 float sbyte byte short ushort int uint long ulong chardecimalFrom float to sbyte, byte, short, ushort, int, uint, long, ulong, char, or decimal.
  • 从到、、、、、、、、、 double sbyte byte short ushort int uint long ulong char floatdecimalFrom double to sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or decimal.
  • 从到、、、、、、、、、 decimal sbyte byte short ushort int uint long ulong char floatdoubleFrom decimal to sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or double.

由于显式转换包括所有隐式和显式的数值转换,因此始终可以使用强制转换表达式 (强制转换) 表达式将任何 numeric_type 转换为任何其他 numeric_typeBecause the explicit conversions include all implicit and explicit numeric conversions, it is always possible to convert from any numeric_type to any other numeric_type using a cast expression (Cast expressions).

显式数值转换可能会丢失信息,或可能导致引发异常。The explicit numeric conversions possibly lose information or possibly cause exceptions to be thrown. 显式数值转换的处理方式如下:An explicit numeric conversion is processed as follows:

  • 对于从整型转换为另一整型类型的转换,该处理依赖于溢出检查上下文 (已检查和未检查的运算符) 发生转换的:For a conversion from an integral type to another integral type, the processing depends on the overflow checking context (The checked and unchecked operators) in which the conversion takes place:
    • checked 上下文中,如果源操作数的值在目标类型的范围内,则转换成功; 但 System.OverflowException 如果源操作数的值超出了目标类型的范围,则会引发。In a checked context, the conversion succeeds if the value of the source operand is within the range of the destination type, but throws a System.OverflowException if the value of the source operand is outside the range of the destination type.
    • unchecked 上下文中,转换始终成功,并按如下所示继续。In an unchecked context, the conversion always succeeds, and proceeds as follows.
      • 如果源类型大于目标类型,则通过放弃其“额外”最高有效位来截断源值。If the source type is larger than the destination type, then the source value is truncated by discarding its "extra" most significant bits. 结果会被视为目标类型的值。The result is then treated as a value of the destination type.
      • 如果源类型小于目标类型,则源值是符号扩展或零扩展,以使其与目标类型的大小相同。If the source type is smaller than the destination type, then the source value is either sign-extended or zero-extended so that it is the same size as the destination type. 如果源类型带符号,则是符号扩展;如果源类型是无符号的,则是零扩展。Sign-extension is used if the source type is signed; zero-extension is used if the source type is unsigned. 结果会被视为目标类型的值。The result is then treated as a value of the destination type.
      • 如果源类型与目标类型的大小相同,则源值将被视为目标类型的值。If the source type is the same size as the destination type, then the source value is treated as a value of the destination type.
  • 对于从 decimal 到整数类型的转换,源值向零舍入到最接近的整数值,并且此整数值将成为转换的结果。For a conversion from decimal to an integral type, the source value is rounded towards zero to the nearest integral value, and this integral value becomes the result of the conversion. 如果生成的整数值超出了目标类型的范围, System.OverflowException 则会引发。If the resulting integral value is outside the range of the destination type, a System.OverflowException is thrown.
  • 对于从 floatdouble 到整型类型的转换,处理操作依赖于溢出检查上下文 (已检查和未检查的运算符) 发生转换的:For a conversion from float or double to an integral type, the processing depends on the overflow checking context (The checked and unchecked operators) in which the conversion takes place:
    • checked 上下文中,转换过程如下所示:In a checked context, the conversion proceeds as follows:
      • 如果操作数的值为 NaN 或无穷大,则 System.OverflowException 会引发。If the value of the operand is NaN or infinite, a System.OverflowException is thrown.
      • 否则,源操作数向零舍入到最接近的整数值。Otherwise, the source operand is rounded towards zero to the nearest integral value. 如果此整数值在目标类型的范围内,则此值为转换的结果。If this integral value is within the range of the destination type then this value is the result of the conversion.
      • 否则,将会引发 System.OverflowExceptionOtherwise, a System.OverflowException is thrown.
    • unchecked 上下文中,转换始终成功,并按如下所示继续。In an unchecked context, the conversion always succeeds, and proceeds as follows.
      • 如果操作数的值为 NaN 或无穷大,则转换的结果是目标类型的未指定值。If the value of the operand is NaN or infinite, the result of the conversion is an unspecified value of the destination type.
      • 否则,源操作数向零舍入到最接近的整数值。Otherwise, the source operand is rounded towards zero to the nearest integral value. 如果此整数值在目标类型的范围内,则此值为转换的结果。If this integral value is within the range of the destination type then this value is the result of the conversion.
      • 否则,转换的结果是目标类型的未指定值。Otherwise, the result of the conversion is an unspecified value of the destination type.
  • 对于从到的 double 转换 float ,此 double 值舍入为最接近的 float 值。For a conversion from double to float, the double value is rounded to the nearest float value. 如果 double 值太小而无法表示为 float ,则结果将变为正零或负零。If the double value is too small to represent as a float, the result becomes positive zero or negative zero. 如果 double 值太大而无法表示为 float ,则结果将变为正无穷或负无穷。If the double value is too large to represent as a float, the result becomes positive infinity or negative infinity. 如果 double 该值为 nan,则结果也为 nan。If the double value is NaN, the result is also NaN.
  • 对于从 float 或到的 double 转换 decimal ,将源值转换为 decimal 表示形式,并在第28位小数后舍入为最接近的数(如果需要 (decimal 类型) 。For a conversion from float or double to decimal, the source value is converted to decimal representation and rounded to the nearest number after the 28th decimal place if required (The decimal type). 如果源值太小而无法表示为 decimal ,则结果将变为零。If the source value is too small to represent as a decimal, the result becomes zero. 如果源值为 NaN、无限大或太大而无法表示为 decimalSystem.OverflowException 则会引发。If the source value is NaN, infinity, or too large to represent as a decimal, a System.OverflowException is thrown.
  • 对于从 decimal 到或的 float 转换 double ,将 decimal 值舍入为最接近 doublefloat 值或值。For a conversion from decimal to float or double, the decimal value is rounded to the nearest double or float value. 虽然这种转换可能会丢失精度,但它永远不会引发异常。While this conversion may lose precision, it never causes an exception to be thrown.

显式枚举转换Explicit enumeration conversions

显式枚举转换为:The explicit enumeration conversions are:

  • sbytebyte 、、、、、、、、、 short ushort int uint long ulong char float doubledecimal 到任何 enum_typeFrom sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, or decimal to any enum_type.
  • 从任何 enum_type 到、、、、、、、、、、 sbyte byte short ushort int uint long ulong char float doubledecimalFrom any enum_type to sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, or decimal.
  • 从任何 enum_type 到任何其他 enum_typeFrom any enum_type to any other enum_type.

处理两种类型之间的显式枚举转换的方式是将任何参与的 enum_type 视为 enum_type 的基础类型,然后在生成的类型之间执行隐式或显式数字转换。An explicit enumeration conversion between two types is processed by treating any participating enum_type as the underlying type of that enum_type, and then performing an implicit or explicit numeric conversion between the resulting types. 例如,假设有一个 E 具有和基础类型的 enum_type,则从到的显式数字转换 int 会将从到的转换 E byte 作为显式数字转换处理 (从到的) 显式数字转换 int byte ,并将从) 到的 byte E 隐式数值转换作为 (隐 byte int 式数值转换处理。For example, given an enum_type E with and underlying type of int, a conversion from E to byte is processed as an explicit numeric conversion (Explicit numeric conversions) from int to byte, and a conversion from byte to E is processed as an implicit numeric conversion (Implicit numeric conversions) from byte to int.

显式可为空转换Explicit nullable conversions

显式可以为 null 的转换 允许对不可以为 null 的值类型进行操作的预定义显式转换也可用于这些类型的可以为 null 的形式。Explicit nullable conversions permit predefined explicit conversions that operate on non-nullable value types to also be used with nullable forms of those types. 对于从不可为 null 的值类型转换为不可为 null 的值类型的每个预定义的显式转换 S T (标识转换隐式数值转换隐式枚举转换显式数字转换显式枚举转换) ,可以为 null 的转换存在:For each of the predefined explicit conversions that convert from a non-nullable value type S to a non-nullable value type T (Identity conversion, Implicit numeric conversions, Implicit enumeration conversions, Explicit numeric conversions, and Explicit enumeration conversions), the following nullable conversions exist:

  • 从到的显式 S? 转换 T?An explicit conversion from S? to T?.
  • 从到的显式 S 转换 T?An explicit conversion from S to T?.
  • 从到的显式 S? 转换 TAn explicit conversion from S? to T.

基于从到的基础转换计算可以为 null 的转换的 S T 过程如下所示:Evaluation of a nullable conversion based on an underlying conversion from S to T proceeds as follows:

  • 如果可以为 null 的转换从 S?T?If the nullable conversion is from S? to T?:
    • 如果源值为 null (HasValue 属性为 false) ,则结果为类型的 null 值 T?If the source value is null (HasValue property is false), the result is the null value of type T?.
    • 否则,转换将作为从到的解包进行计算 S? S ,后跟从到的 S 转换 T ,后跟从到的换行 T T?Otherwise, the conversion is evaluated as an unwrapping from S? to S, followed by the underlying conversion from S to T, followed by a wrapping from T to T?.
  • 如果可以为 null 的转换从 ST? ,则会将转换计算为从到的基础转换, S 并将从到 T 的换行 T T?If the nullable conversion is from S to T?, the conversion is evaluated as the underlying conversion from S to T followed by a wrapping from T to T?.
  • 如果从到的可为 null 的转换为 S? T ,则会将转换计算为从到的解包, S? S 后跟从到的基础转换 S TIf the nullable conversion is from S? to T, the conversion is evaluated as an unwrapping from S? to S followed by the underlying conversion from S to T.

请注意,如果值为,则对可以为 null 的值进行解包的尝试将引发异常 nullNote that an attempt to unwrap a nullable value will throw an exception if the value is null.

显式引用转换Explicit reference conversions

显式引用转换为:The explicit reference conversions are:

  • objectdynamic 到任何其他 reference_typeFrom object and dynamic to any other reference_type.
  • 从任何 class_type S 到任何 class_type T ,提供的 S 是的基类 TFrom any class_type S to any class_type T, provided S is a base class of T.
  • 从任何 class_type S 到任何 interface_type T ,提供 S 的不是密封的,并且不 S 会实现 TFrom any class_type S to any interface_type T, provided S is not sealed and provided S does not implement T.
  • 从任何 interface_type S 到任何 class_type T T 均未密封或未提供 T 实现 SFrom any interface_type S to any class_type T, provided T is not sealed or provided T implements S.
  • 从任何 interface_type S 到任何 interface_type T ,提供 S 的不是从派生的 TFrom any interface_type S to any interface_type T, provided S is not derived from T.
  • 在 array_type 具有元素类型的 array_typeS ,如果 SE T TE 满足以下所有条件:From an array_type S with an element type SE to an array_type T with an element type TE, provided all of the following are true:
    • ST 仅因元素类型而异。S and T differ only in element type. 换言之, ST 具有相同的维数。In other words, S and T have the same number of dimensions.
    • SETE 都是 reference_typeBoth SE and TE are reference_type s.
    • 存在从到的显式引用 SE 转换 TEAn explicit reference conversion exists from SE to TE.
  • System.Array 及其实现的接口到任何 array_typeFrom System.Array and the interfaces it implements to any array_type.
  • 从一维数组类型 S[]System.Collections.Generic.IList<T> 及其基接口,前提是存在从到的显式引用转换 S TFrom a single-dimensional array type S[] to System.Collections.Generic.IList<T> and its base interfaces, provided that there is an explicit reference conversion from S to T.
  • System.Collections.Generic.IList<S> 及其基接口到一维数组类型 T[] ,前提是存在从到的显式标识或引用转换 S TFrom System.Collections.Generic.IList<S> and its base interfaces to a single-dimensional array type T[], provided that there is an explicit identity or reference conversion from S to T.
  • System.Delegate 及其实现的接口到任何 delegate_typeFrom System.Delegate and the interfaces it implements to any delegate_type.
  • 如果引用类型 T 具有到引用类型的显式引用转换 T0 并且 T0 具有标识转换,则从引用类型转换为引用类型 TFrom a reference type to a reference type T if it has an explicit reference conversion to a reference type T0 and T0 has an identity conversion T.
  • 如果引用类型 T 具有到接口或委托类型的显式引用转换,则从引用类型转换为接口或委托类型 T0 ,或者可以转换为 (方差 T0 T T T0 转换) 的变体。From a reference type to an interface or delegate type T if it has an explicit reference conversion to an interface or delegate type T0 and either T0 is variance-convertible to T or T is variance-convertible to T0 (Variance conversion).
  • D<S1...Sn> 到, D<T1...Tn> 其中 D<X1...Xn> 是泛型委托类型,与 D<S1...Sn> D<T1...Tn> 以下类型的每个类型参数均不兼容或不相同 Xi DFrom D<S1...Sn> to D<T1...Tn> where D<X1...Xn> is a generic delegate type, D<S1...Sn> is not compatible with or identical to D<T1...Tn>, and for each type parameter Xi of D the following holds:
    • 如果 Xi 是固定的,则与 Si 相同 TiIf Xi is invariant, then Si is identical to Ti.
    • 如果 Xi 是协变的,则存在隐式或显式标识或从到的引用转换 Si TiIf Xi is covariant, then there is an implicit or explicit identity or reference conversion from Si to Ti.
    • 如果 Xi 为逆变,则 SiTi 都是相同或同时为这两个引用类型。If Xi is contravariant, then Si and Ti are either identical or both reference types.
  • 涉及称为引用类型的类型参数的显式转换。Explicit conversions involving type parameters that are known to be reference types. 有关涉及类型参数的显式转换的详细信息,请参阅 涉及类型参数的显式转换For more details on explicit conversions involving type parameters, see Explicit conversions involving type parameters.

显式引用转换是需要运行时检查以确保它们正确的引用类型之间的转换。The explicit reference conversions are those conversions between reference-types that require run-time checks to ensure they are correct.

若要在运行时成功进行显式引用转换,源操作数的值必须为 null ,或者源操作数引用的对象的实际类型必须是可通过隐式引用转换转换为目标类型的类型, (隐式引用 转换) 或装箱转换 (装箱 转换) 。For an explicit reference conversion to succeed at run-time, the value of the source operand must be null, or the actual type of the object referenced by the source operand must be a type that can be converted to the destination type by an implicit reference conversion (Implicit reference conversions) or boxing conversion (Boxing conversions). 如果显式引用转换失败, System.InvalidCastException 将引发。If an explicit reference conversion fails, a System.InvalidCastException is thrown.

引用转换、隐式或显式转换决不会更改正在转换的对象的引用标识。Reference conversions, implicit or explicit, never change the referential identity of the object being converted. 换言之,虽然引用转换可以更改引用的类型,但它不会更改引用的对象的类型或值。In other words, while a reference conversion may change the type of the reference, it never changes the type or value of the object being referred to.

取消装箱转换Unboxing conversions

取消装箱转换允许将引用类型显式转换为 value_typeAn unboxing conversion permits a reference type to be explicitly converted to a value_type. 从类型 object dynamicSystem.ValueType 任何 non_nullable_value_type,以及从任何 interface_type 到实现 interface_type 的任何 non_nullable_value_type 的取消装箱转换。An unboxing conversion exists from the types object, dynamic and System.ValueType to any non_nullable_value_type, and from any interface_type to any non_nullable_value_type that implements the interface_type. 此外 System.Enum ,可以将类型取消装箱为任意 enum_typeFurthermore type System.Enum can be unboxed to any enum_type.

如果从引用类型到 nullable_type 的基础 non_nullable_value_type 的取消装箱转换存在,则取消装箱转换将从引用类型转换为 nullable_typeAn unboxing conversion exists from a reference type to a nullable_type if an unboxing conversion exists from the reference type to the underlying non_nullable_value_type of the nullable_type.

如果值类型 S I 具有从接口类型的取消装箱转换 I0 ,并且其 I0 标识转换为,则值类型会从接口类型进行取消装箱转换 IA value type S has an unboxing conversion from an interface type I if it has an unboxing conversion from an interface type I0 and I0 has an identity conversion to I.

如果某个值类型 S I 具有从接口或委托类型的取消装箱转换,并且该类型的变体转换为 I0 I0 I I I0 (方差转换) ,则它会从接口类型进行取消装箱转换。A value type S has an unboxing conversion from an interface type I if it has an unboxing conversion from an interface or delegate type I0 and either I0 is variance-convertible to I or I is variance-convertible to I0 (Variance conversion).

取消装箱操作包括:首先检查对象实例是否是给定 value_type 的装箱值,然后将值复制到该实例之外。An unboxing operation consists of first checking that the object instance is a boxed value of the given value_type, and then copying the value out of the instance. 取消装箱对 nullable_type 的空引用将生成 nullable_type 的 null 值。Unboxing a null reference to a nullable_type produces the null value of the nullable_type. 结构可以从类型取消装箱 System.ValueType ,因为这是所有结构的基类 (继承) 。A struct can be unboxed from the type System.ValueType, since that is a base class for all structs (Inheritance).

取消装箱转换中进一步介绍 了取消装箱转换。Unboxing conversions are described further in Unboxing conversions.

显式动态转换Explicit dynamic conversions

存在从类型为的表达式 dynamic 到任何类型的显式动态转换 TAn explicit dynamic conversion exists from an expression of type dynamic to any type T. 转换是动态绑定 (动态绑定) ,这意味着将在运行时从表达式的运行时类型中查找显式转换 TThe conversion is dynamically bound (Dynamic binding), which means that an explicit conversion will be sought at run-time from the run-time type of the expression to T. 如果未找到任何转换,则会引发运行时异常。If no conversion is found, a run-time exception is thrown.

如果不需要转换的动态绑定,则可以先将该表达式转换为 object ,然后再转换为所需的类型。If dynamic binding of the conversion is not desired, the expression can be first converted to object, and then to the desired type.

假定定义了下面的类:Assume the following class is defined:

class C
{
    int i;

    public C(int i) { this.i = i; }

    public static explicit operator C(string s) 
    {
        return new C(int.Parse(s));
    }
}

下面的示例阐释了显式动态转换:The following example illustrates explicit dynamic conversions:

object o  = "1";
dynamic d = "2";

var c1 = (C)o; // Compiles, but explicit reference conversion fails
var c2 = (C)d; // Compiles and user defined conversion succeeds

在编译时,将转换为的最佳转换为 o C 显式引用转换。The best conversion of o to C is found at compile-time to be an explicit reference conversion. 这会在运行时失败,因为 "1" 事实上并不是 CThis fails at run-time, because "1" is not in fact a C. 但是,将转换为,以 d C 显式动态转换为运行时,将在其中找到用户定义的运行时类型到的转换 d -- string C ,并成功。The conversion of d to C however, as an explicit dynamic conversion, is suspended to run-time, where a user defined conversion from the run-time type of d -- string -- to C is found, and succeeds.

涉及类型参数的显式转换Explicit conversions involving type parameters

给定类型参数存在以下显式转换 TThe following explicit conversions exist for a given type parameter T:

  • 从的有效基类 C T ,到的 T 任何基类 C TFrom the effective base class C of T to T and from any base class of C to T. 在运行时,如果 T 是值类型,则转换将作为取消装箱转换执行。At run-time, if T is a value type, the conversion is executed as an unboxing conversion. 否则,转换将作为显式引用转换或标识转换执行。Otherwise, the conversion is executed as an explicit reference conversion or identity conversion.
  • 从任何接口类型到 TFrom any interface type to T. 在运行时,如果 T 是值类型,则转换将作为取消装箱转换执行。At run-time, if T is a value type, the conversion is executed as an unboxing conversion. 否则,转换将作为显式引用转换或标识转换执行。Otherwise, the conversion is executed as an explicit reference conversion or identity conversion.
  • T I 如果尚未从到的隐式转换,则从到任何 T interface_type IFrom T to any interface_type I provided there is not already an implicit conversion from T to I. 在运行时,如果 T 是值类型,则转换将作为装箱转换执行,后跟显式引用转换。At run-time, if T is a value type, the conversion is executed as a boxing conversion followed by an explicit reference conversion. 否则,转换将作为显式引用转换或标识转换执行。Otherwise, the conversion is executed as an explicit reference conversion or identity conversion.
  • 从类型参数 UT ,前提条件 T 取决于 U) (类型参数约束From a type parameter U to T, provided T depends on U (Type parameter constraints). 在运行时,如果 U 是值类型,则和的 T U 类型必须相同,并且不执行任何转换。At run-time, if U is a value type, then T and U are necessarily the same type and no conversion is performed. 否则,如果 T 是值类型,则转换将作为取消装箱转换执行。Otherwise, if T is a value type, the conversion is executed as an unboxing conversion. 否则,转换将作为显式引用转换或标识转换执行。Otherwise, the conversion is executed as an explicit reference conversion or identity conversion.

如果 T 已知为引用类型,则上述转换均分类为显式引用转换 () 的 显式引用转换If T is known to be a reference type, the conversions above are all classified as explicit reference conversions (Explicit reference conversions). 如果 T 未知类型为引用类型,则上述转换归类为取消装箱转换 (取消装箱转换) 。If T is not known to be a reference type, the conversions above are classified as unboxing conversions (Unboxing conversions).

以上规则不允许从不受约束的类型形参直接转换为非接口类型,这可能会令人吃惊。The above rules do not permit a direct explicit conversion from an unconstrained type parameter to a non-interface type, which might be surprising. 此规则的原因是为了防止混淆并使此类转换的语义清晰。The reason for this rule is to prevent confusion and make the semantics of such conversions clear. 例如,请考虑以下声明:For example, consider the following declaration:

class X<T>
{
    public static long F(T t) {
        return (long)t;                // Error 
    }
}

如果允许将的直接显式转换 tint ,则很可能会出现这样的情况 X<int>.F(7)  7LIf the direct explicit conversion of t to int were permitted, one might easily expect that X<int>.F(7) would return 7L. 但是,它不会这样,因为仅当已知类型在绑定时是数字时才考虑标准数字转换。However, it would not, because the standard numeric conversions are only considered when the types are known to be numeric at binding-time. 为了使语义清晰明了,必须改为编写以上示例:In order to make the semantics clear, the above example must instead be written:

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 无法直接转换为 longThis code will now compile but executing X<int>.F(7) would then throw an exception at run-time, since a boxed int cannot be converted directly to a long.

用户定义的显式转换User-defined explicit conversions

用户定义的显式转换包含一个可选的标准显式转换,然后执行用户定义的隐式或显式转换运算符,然后执行另一个可选的标准显式转换。A user-defined explicit conversion consists of an optional standard explicit conversion, followed by execution of a user-defined implicit or explicit conversion operator, followed by another optional standard explicit conversion. 用于评估用户定义的显式转换的确切规则在 处理用户定义的显式转换中进行了介绍。The exact rules for evaluating user-defined explicit conversions are described in Processing of user-defined explicit conversions.

标准转换Standard conversions

标准转换是那些预定义的转换,这些转换可作为用户定义的转换的一部分出现。The standard conversions are those pre-defined conversions that can occur as part of a user-defined conversion.

标准隐式转换Standard implicit conversions

以下隐式转换归类为标准隐式转换:The following implicit conversions are classified as standard implicit conversions:

标准隐式转换专门排除用户定义的隐式转换。The standard implicit conversions specifically exclude user-defined implicit conversions.

标准显式转换Standard explicit conversions

标准显式转换均为标准隐式转换,以及与之相反的标准隐式转换的显式转换的子集。The standard explicit conversions are all standard implicit conversions plus the subset of the explicit conversions for which an opposite standard implicit conversion exists. 换言之,如果存在从类型到类型的标准隐式转换 A B ,则从类型 A 到类型 B 以及从类型到类型的标准显式转换 B AIn other words, if a standard implicit conversion exists from a type A to a type B, then a standard explicit conversion exists from type A to type B and from type B to type A.

用户定义的转换User-defined conversions

C # 允许使用 用户定义的转换 来扩充预定义的隐式和显式转换。C# allows the pre-defined implicit and explicit conversions to be augmented by user-defined conversions. 用户定义的转换是通过在类和结构类型中 (转换运算符) 声明转换运算符引入的。User-defined conversions are introduced by declaring conversion operators (Conversion operators) in class and struct types.

允许的用户定义的转换Permitted user-defined conversions

C # 只允许声明某些用户定义的转换。C# permits only certain user-defined conversions to be declared. 特别是,不能重新定义已存在的隐式或显式转换。In particular, it is not possible to redefine an already existing implicit or explicit conversion.

对于给定的源类型 S 和目标类型 T ,如果 ST 是可以为 null 的类型,则让 S0T0 引用它们的基础类型,否则, S0T0 S T 分别为和。For a given source type S and target type T, if S or T are nullable types, let S0 and T0 refer to their underlying types, otherwise S0 and T0 are equal to S and T respectively. S T 仅当满足以下所有条件时,才允许类或结构声明从源类型到目标类型的转换:A class or struct is permitted to declare a conversion from a source type S to a target type T only if all of the following are true:

  • S0T0 是不同的类型。S0 and T0 are different types.
  • S0T0 是发生运算符声明的类或结构类型。Either S0 or T0 is the class or struct type in which the operator declaration takes place.
  • S0T0 都不是 interface_typeNeither S0 nor T0 is an interface_type.
  • 如果不包括用户定义的转换,则从到的转换不存在 S T T SExcluding user-defined conversions, a conversion does not exist from S to T or from T to S.

适用于用户定义的转换的限制将在 转换运算符中进一步讨论。The restrictions that apply to user-defined conversions are discussed further in Conversion operators.

提升的转换运算符Lifted conversion operators

给定用户定义的转换运算符,该运算符将不可以为 null 的值类型转换 S 为不可为 null 的值类型 T ,并且存在将从转换为的 提升转换运算符 S? T?Given a user-defined conversion operator that converts from a non-nullable value type S to a non-nullable value type T, a lifted conversion operator exists that converts from S? to T?. 此提升转换运算符执行从到的解包,然后执行从到的 S? S 用户定义的转换 S T ,后跟从到的换行 T T? ,只不过空值 S? 直接转换为 null 值 T?This lifted conversion operator performs an unwrapping from S? to S followed by the user-defined conversion from S to T followed by a wrapping from T to T?, except that a null valued S? converts directly to a null valued T?.

提升的转换运算符与其基础用户定义转换运算符具有相同的隐式或显式分类。A lifted conversion operator has the same implicit or explicit classification as its underlying user-defined conversion operator. "用户定义的转换" 一词适用于用户定义的转换运算符和提升的转换运算符。The term "user-defined conversion" applies to the use of both user-defined and lifted conversion operators.

计算用户定义的转换Evaluation of user-defined conversions

用户定义的转换将其类型(称为 源类型 _)的值转换为另一种类型,称为 _目标类型*A user-defined conversion converts a value from its type, called the source type _, to another type, called the _target type*. 计算用户定义的转换中心,查找特定源和目标类型的 _ 最具体 的 * 用户定义转换运算符。Evaluation of a user-defined conversion centers on finding the _ most specific* user-defined conversion operator for the particular source and target types. 此确定分为几个步骤:This determination is broken into several steps:

  • 查找要将用户定义的转换运算符视为其中的一组类和结构。Finding the set of classes and structs from which user-defined conversion operators will be considered. 此集由源类型及其基类和目标类型及其基类 (,这些隐式假设只有类和结构可以声明用户定义的运算符,并且非类类型没有基类) 。This set consists of the source type and its base classes and the target type and its base classes (with the implicit assumptions that only classes and structs can declare user-defined operators, and that non-class types have no base classes). 对于此步骤,如果源类型或目标类型为 nullable_type,则改为使用它们的基础类型。For the purposes of this step, if either the source or target type is a nullable_type, their underlying type is used instead.
  • 从该类型集中确定哪些用户定义的转换运算符适用。From that set of types, determining which user-defined and lifted conversion operators are applicable. 要使转换运算符适用,必须可以执行标准转换 (标准 转换) 从源类型转换为运算符的操作数类型,并且必须能够执行从运算符的结果类型到目标类型的标准转换操作。For a conversion operator to be applicable, it must be possible to perform a standard conversion (Standard conversions) from the source type to the operand type of the operator, and it must be possible to perform a standard conversion from the result type of the operator to the target type.
  • 从一组适用的用户定义的运算符,确定哪个运算符明确是最具体的。From the set of applicable user-defined operators, determining which operator is unambiguously the most specific. 一般来说,最特定的运算符是操作数类型与源类型 "最接近" 的运算符,其结果类型与目标类型 "最近"。In general terms, the most specific operator is the operator whose operand type is "closest" to the source type and whose result type is "closest" to the target type. 用户定义的转换运算符优先于提升转换运算符。User-defined conversion operators are preferred over lifted conversion operators. 以下部分定义了用于建立最具体的用户定义转换运算符的确切规则。The exact rules for establishing the most specific user-defined conversion operator are defined in the following sections.

确定了最特定的用户定义转换运算符后,用户定义的转换的实际执行操作包括多达三个步骤:Once a most specific user-defined conversion operator has been identified, the actual execution of the user-defined conversion involves up to three steps:

  • 首先,如果需要,执行从源类型到用户定义或提升的转换运算符的操作数类型的标准转换。First, if required, performing a standard conversion from the source type to the operand type of the user-defined or lifted conversion operator.
  • 接下来,调用用户定义或提升的转换运算符来执行转换。Next, invoking the user-defined or lifted conversion operator to perform the conversion.
  • 最后,如果需要,执行从用户定义转换运算符的结果类型到目标类型的标准转换。Finally, if required, performing a standard conversion from the result type of the user-defined or lifted conversion operator to the target type.

用户定义的转换的计算从不涉及多个用户定义的转换运算符或提升的转换运算符。Evaluation of a user-defined conversion never involves more than one user-defined or lifted conversion operator. 换句话说,从类型到类型的转换 S T 从不首先执行从到的用户定义的转换 S X ,然后执行从到的用户定义的转换 X TIn other words, a conversion from type S to type T will never first execute a user-defined conversion from S to X and then execute a user-defined conversion from X to T.

以下各节提供了用户定义的隐式或显式转换的确切计算定义。Exact definitions of evaluation of user-defined implicit or explicit conversions are given in the following sections. 定义使用以下术语:The definitions make use of the following terms:

  • 如果标准隐式转换 (从类型到类型的标准 隐式转换) A B ,并且和都不 A B interface_type,则称为 A *包含 _ B ,并 B 被称为 _ 包含 * AIf a standard implicit conversion (Standard implicit conversions) exists from a type A to a type B, and if neither A nor B are interface_type s, then A is said to be encompassed by _ B, and B is said to _ encompass A.
  • 一组类型中包含的 最大的类型 是一种包含集内所有其他类型的类型。The most encompassing type in a set of types is the one type that encompasses all other types in the set. 如果没有一种类型包含所有其他类型,则该集没有最大的包含类型。If no single type encompasses all other types, then the set has no most encompassing type. 更直观地说,最包含的类型是集中的 "最大" 类型,每个其他类型都可以隐式转换为一种类型。In more intuitive terms, the most encompassing type is the "largest" type in the set—the one type to which each of the other types can be implicitly converted.
  • 类型集中 最常被包含的类型 是一种类型,它由集中的所有其他类型所包含。The most encompassed type in a set of types is the one type that is encompassed by all other types in the set. 如果所有其他类型都不包含任何一种类型,则该集不包含大多数被包含的类型。If no single type is encompassed by all other types, then the set has no most encompassed type. 更直观地说,最包含的类型是集中的 "最小" 类型,这种类型可以隐式转换为其他类型。In more intuitive terms, the most encompassed type is the "smallest" type in the set—the one type that can be implicitly converted to each of the other types.

处理用户定义的隐式转换Processing of user-defined implicit conversions

用户定义的从类型到类型的隐式转换的 S T 处理方式如下:A user-defined implicit conversion from type S to type T is processed as follows:

  • 确定类型 S0T0Determine the types S0 and T0. 如果 ST 是可以为 null 的类型, S0 并且 T0 是其基础类型,则为; 否则 S0 T0 分别为和 S TIf S or T are nullable types, S0 and T0 are their underlying types, otherwise S0 and T0 are equal to S and T respectively.
  • 查找类型集, D 用户定义的转换运算符将从其考虑。Find the set of types, D, from which user-defined conversion operators will be considered. S0如果是类或结构) ,则此集由 (组成 S0 , (的基类( S0 如果 S0 是类) )和 T0 ((如果 T0 是类或结构) )。This set consists of S0 (if S0 is a class or struct), the base classes of S0 (if S0 is a class), and T0 (if T0 is a class or struct).
  • 查找适用的用户定义转换运算符和提升转换运算符集 UFind the set of applicable user-defined and lifted conversion operators, U. 此集由中的类或结构所声明的用户定义的和提升的隐式转换运算符组成,这些类或结构从包含的类型包含在中 D S TThis set consists of the user-defined and lifted implicit conversion operators declared by the classes or structs in D that convert from a type encompassing S to a type encompassed by T. 如果 U 为空,则不定义转换,并发生编译时错误。If U is empty, the conversion is undefined and a compile-time error occurs.
  • 查找中运算符的最特定源类型 SX UFind the most specific source type, SX, of the operators in U:
    • 如果中的任何运算符 U S 都为,则 SXSIf any of the operators in U convert from S, then SX is S.
    • 否则, SX 是中运算符的组合源类型集内包含程度最高的类型 UOtherwise, SX is the most encompassed type in the combined set of source types of the operators in U. 如果找不到完全包含的类型,则转换是不明确的,并发生编译时错误。If exactly one most encompassed type cannot be found, then the conversion is ambiguous and a compile-time error occurs.
  • 查找中运算符的最特定目标类型 TX UFind the most specific target type, TX, of the operators in U:
    • 如果中的任何运算符 U 转换为 T ,则 TXTIf any of the operators in U convert to T, then TX is T.
    • 否则, TX 是中运算符的组合目标类型集中的最包含的类型 UOtherwise, TX is the most encompassing type in the combined set of target types of the operators in U. 如果找不到完全包含的类型,则转换是不明确的,并发生编译时错误。If exactly one most encompassing type cannot be found, then the conversion is ambiguous and a compile-time error occurs.
  • 查找最具体的转换运算符:Find the most specific conversion operator:
    • 如果 U 只包含一个从转换为的用户定义的转换 SX 运算符 TX ,则这是最具体的转换运算符。If U contains exactly one user-defined conversion operator that converts from SX to TX, then this is the most specific conversion operator.
    • 否则,如果 U 只包含一个从转换为的提升转换 SX 运算符 TX ,则这是最具体的转换运算符。Otherwise, if U contains exactly one lifted conversion operator that converts from SX to TX, then this is the most specific conversion operator.
    • 否则,转换是不明确的,并发生编译时错误。Otherwise, the conversion is ambiguous and a compile-time error occurs.
  • 最后,应用转换:Finally, apply the conversion:
    • 如果不 SSX ,则执行从到的标准隐式转换 S SXIf S is not SX, then a standard implicit conversion from S to SX is performed.
    • 调用最特定的转换运算符,以将转换 SXTXThe most specific conversion operator is invoked to convert from SX to TX.
    • 如果不 TXT ,则执行从到的标准隐式转换 TX TIf TX is not T, then a standard implicit conversion from TX to T is performed.

处理用户定义的显式转换Processing of user-defined explicit conversions

用户定义的从类型到类型的显式转换的 S T 处理方式如下:A user-defined explicit conversion from type S to type T is processed as follows:

  • 确定类型 S0T0Determine the types S0 and T0. 如果 ST 是可以为 null 的类型, S0 并且 T0 是其基础类型,则为; 否则 S0 T0 分别为和 S TIf S or T are nullable types, S0 and T0 are their underlying types, otherwise S0 and T0 are equal to S and T respectively.
  • 查找类型集, D 用户定义的转换运算符将从其考虑。Find the set of types, D, from which user-defined conversion operators will be considered. S0如果是类或结构) ,则此集由 (,如果是类 S0 或结构、 (的基类( S0 如果是类 S0) )、 T0 (if T0 是类或结构) ,以及 (的基类( T0 如果 T0 是类) )。This set consists of S0 (if S0 is a class or struct), the base classes of S0 (if S0 is a class), T0 (if T0 is a class or struct), and the base classes of T0 (if T0 is a class).
  • 查找适用的用户定义转换运算符和提升转换运算符集 UFind the set of applicable user-defined and lifted conversion operators, U. 此集由中由的类或结构声明的用户定义的隐式或显式转换运算符,由中的 D 类型转换 S 为包含或包含的类型 TThis set consists of the user-defined and lifted implicit or explicit conversion operators declared by the classes or structs in D that convert from a type encompassing or encompassed by S to a type encompassing or encompassed by T. 如果 U 为空,则不定义转换,并发生编译时错误。If U is empty, the conversion is undefined and a compile-time error occurs.
  • 查找中运算符的最特定源类型 SX UFind the most specific source type, SX, of the operators in U:
    • 如果中的任何运算符 U S 都为,则 SXSIf any of the operators in U convert from S, then SX is S.
    • 否则,如果中的任何运算符 U 从包含的类型转换 S ,则 SX 是这些运算符的组合源类型集中包含程度最高的类型。Otherwise, if any of the operators in U convert from types that encompass S, then SX is the most encompassed type in the combined set of source types of those operators. 如果找不到最能包含的类型,则转换是不明确的,并发生编译时错误。If no most encompassed type can be found, then the conversion is ambiguous and a compile-time error occurs.
    • 否则, SX 是中运算符的组合源类型集中的最包含的类型 UOtherwise, SX is the most encompassing type in the combined set of source types of the operators in U. 如果找不到完全包含的类型,则转换是不明确的,并发生编译时错误。If exactly one most encompassing type cannot be found, then the conversion is ambiguous and a compile-time error occurs.
  • 查找中运算符的最特定目标类型 TX UFind the most specific target type, TX, of the operators in U:
    • 如果中的任何运算符 U 转换为 T ,则 TXTIf any of the operators in U convert to T, then TX is T.
    • 否则,如果中的任何运算符 U 转换为所包含的类型,则 T TX 是这些运算符的组合目标类型集中最包含的类型。Otherwise, if any of the operators in U convert to types that are encompassed by T, then TX is the most encompassing type in the combined set of target types of those operators. 如果找不到完全包含的类型,则转换是不明确的,并发生编译时错误。If exactly one most encompassing type cannot be found, then the conversion is ambiguous and a compile-time error occurs.
    • 否则, TX 是中运算符的组合目标类型集中的包含程度最高的类型 UOtherwise, TX is the most encompassed type in the combined set of target types of the operators in U. 如果找不到最能包含的类型,则转换是不明确的,并发生编译时错误。If no most encompassed type can be found, then the conversion is ambiguous and a compile-time error occurs.
  • 查找最具体的转换运算符:Find the most specific conversion operator:
    • 如果 U 只包含一个从转换为的用户定义的转换 SX 运算符 TX ,则这是最具体的转换运算符。If U contains exactly one user-defined conversion operator that converts from SX to TX, then this is the most specific conversion operator.
    • 否则,如果 U 只包含一个从转换为的提升转换 SX 运算符 TX ,则这是最具体的转换运算符。Otherwise, if U contains exactly one lifted conversion operator that converts from SX to TX, then this is the most specific conversion operator.
    • 否则,转换是不明确的,并发生编译时错误。Otherwise, the conversion is ambiguous and a compile-time error occurs.
  • 最后,应用转换:Finally, apply the conversion:
    • 如果不 SSX ,则执行从到的标准显式转换 S SXIf S is not SX, then a standard explicit conversion from S to SX is performed.
    • 调用最特定的用户定义转换运算符,以将从转换 SXTXThe most specific user-defined conversion operator is invoked to convert from SX to TX.
    • 如果不 TXT ,则执行从到的标准显式转换 TX TIf TX is not T, then a standard explicit conversion from TX to T is performed.

匿名函数转换Anonymous function conversions

Anonymous_method_expressionlambda_expression 归类为匿名函数 (匿名函数表达式) 。An anonymous_method_expression or lambda_expression is classified as an anonymous function (Anonymous function expressions). 表达式没有类型,但可以隐式转换为兼容的委托类型或表达式树类型。The expression does not have a type but can be implicitly converted to a compatible delegate type or expression tree type. 具体而言,匿名函数 F 与所提供的委托类型兼容 DSpecifically, an anonymous function F is compatible with a delegate type D provided:

  • 如果 F 包含一个 anonymous_function_signature,则 DF 具有相同数量的参数。If F contains an anonymous_function_signature, then D and F have the same number of parameters.
  • 如果不 F 包含 anonymous_function_signature,则 D 只要没有的参数 D 具有参数修饰符,就可以具有零个或多个任意类型的参数 outIf F does not contain an anonymous_function_signature, then D may have zero or more parameters of any type, as long as no parameter of D has the out parameter modifier.
  • 如果 F 具有显式类型化参数列表,则中的每个参数 D 都具有与中相应参数相同的类型和修饰符 FIf F has an explicitly typed parameter list, each parameter in D has the same type and modifiers as the corresponding parameter in F.
  • 如果 F 具有隐式类型的参数列表,则 D 不包含 refout 参数。If F has an implicitly typed parameter list, D has no ref or out parameters.
  • 如果的主体 F 是一个表达式,并且 D 具有 void 返回类型或 F 为 async 并且 D 具有返回类型 Task ,则当的每个参数都为 F 指定中的相应参数的类型时,的 D 主体 F 为 (wrt 表达式) 的有效表达式,该表达式将允许作为) 的 statement_expression (表达式语句If the body of F is an expression, and either D has a void return type or F is async and D has the return type Task, then when each parameter of F is given the type of the corresponding parameter in D, the body of F is a valid expression (wrt Expressions) that would be permitted as a statement_expression (Expression statements).
  • 如果的主体 F 是一个语句块,并且 D 具有 void 返回类型或 F 为 async 并且 D 具有返回类型 Task ,则当的每个参数都为 F 指定中的相应参数的类型时,的 D 正文 F 为有效语句块 (wrt ) ,其中没有 return 语句指定表达式。If the body of F is a statement block, and either D has a void return type or F is async and D has the return type Task, then when each parameter of F is given the type of the corresponding parameter in D, the body of F is a valid statement block (wrt Blocks) in which no return statement specifies an expression.
  • 如果的主体 F 是一个表达式,并且 F 为非 async 并且 D 具有非 void 返回类型 T或者 F 是异步的,并且 D 具有返回类型,则 Task<T> 当的每个参数 F 都为指定中的相应参数的类型时,的 D 主体 F 将是有效的表达式, (可隐 式转换为) 的 wrt 表达式 TIf the body of F is an expression, and either F is non-async and D has a non-void return type T, or F is async and D has a return type Task<T>, then when each parameter of F is given the type of the corresponding parameter in D, the body of F is a valid expression (wrt Expressions) that is implicitly convertible to T.
  • 如果的主体 F 是语句块,和 F 为非 async 并且 D 具有非 void 返回类型 T或者 F 是异步的,并且 D 具有返回类型,则 Task<T> 当的每个参数的类型为 F 中的相应参数时 D ,的主体 F 为有效语句块 (wrt 块 其中每个语句都) return 指定一个可隐式转换为的表达式 TIf the body of F is a statement block, and either F is non-async and D has a non-void return type T, or F is async and D has a return type Task<T>, then when each parameter of F is given the type of the corresponding parameter in D, the body of F is a valid statement block (wrt Blocks) with a non-reachable end point in which each return statement specifies an expression that is implicitly convertible to T.

为了简洁起见,本部分对任务类型 TaskTask<T> (异步函数) 使用缩写形式。For the purpose of brevity, this section uses the short form for the task types Task and Task<T> (Async functions).

F Expression<D> 如果与 F 委托类型兼容,则 lambda 表达式与表达式树类型兼容 DA lambda expression F is compatible with an expression tree type Expression<D> if F is compatible with the delegate type D. 请注意,这不适用于匿名方法,只适用于 lambda 表达式。Note that this does not apply to anonymous methods, only lambda expressions.

某些 lambda 表达式不能转换为表达式树类型:即使转换 存在,它也会在编译时失败。Certain lambda expressions cannot be converted to expression tree types: Even though the conversion exists, it fails at compile-time. 如果 lambda 表达式为,则为这种情况:This is the case if the lambda expression:

  • 具有 Has a block body
  • 包含简单赋值运算符或复合赋值运算符Contains simple or compound assignment operators
  • 包含动态绑定的表达式Contains a dynamically bound expression
  • 为异步Is async

下面的示例使用泛型委托类型, Func<A,R> 该类型表示一个函数,该函数采用类型的参数 A 并返回类型的值 RThe examples that follow use a generic delegate type Func<A,R> which represents a function that takes an argument of type A and returns a value of type R:

delegate R Func<A,R>(A arg);

在分配中In the assignments

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

每个匿名函数的参数和返回类型都是从匿名函数所分配到的变量类型确定的。the parameter and return types of each anonymous function are determined from the type of the variable to which the anonymous function is assigned.

第一次分配成功将匿名函数转换为委托类型 Func<int,int> ,因为当 x 给定类型时 intx+1 是可隐式转换为类型的有效表达式 intThe first assignment successfully converts the anonymous function to the delegate type Func<int,int> because, when x is given type int, x+1 is a valid expression that is implicitly convertible to type int.

同样,第二个赋值成功将匿名函数转换为委托类型, Func<int,double> 因为) 类型的 (的结果可 x+1 int 隐式转换为 double 类型。Likewise, the second assignment successfully converts the anonymous function to the delegate type Func<int,double> because the result of x+1 (of type int) is implicitly convertible to type double.

但是,第三个赋值是编译时错误,因为当 x 给定类型时, double x+1 类型) (的结果 double 无法隐式转换为类型 intHowever, the third assignment is a compile-time error because, when x is given type double, the result of x+1 (of type double) is not implicitly convertible to type int.

第四个赋值成功将匿名 async 函数转换为委托类型, Func<int, Task<int>> 因为) 类型的 (的结果可 x+1 int 隐式转换为该 int 任务类型的结果类型 Task<int>The fourth assignment successfully converts the anonymous async function to the delegate type Func<int, Task<int>> because the result of x+1 (of type int) is implicitly convertible to the result type int of the task type Task<int>.

匿名函数可能会影响重载决策,并参与类型推理。Anonymous functions may influence overload resolution, and participate in type inference. 有关更多详细信息,请参阅 函数成员See Function members for further details.

匿名函数转换到委托类型的计算Evaluation of anonymous function conversions to delegate types

将匿名函数转换为委托类型会生成一个委托实例,该实例引用匿名函数,并且 (可能为空,这是在计算时处于活动状态的已捕获外部变量的) 集。Conversion of an anonymous function to a delegate type produces a delegate instance which references the anonymous function and the (possibly empty) set of captured outer variables that are active at the time of the evaluation. 调用委托时,将执行匿名函数的主体。When the delegate is invoked, the body of the anonymous function is executed. 使用委托引用的捕获外部变量集来执行正文中的代码。The code in the body is executed using the set of captured outer variables referenced by the delegate.

从匿名函数生成的委托的调用列表包含单个项。The invocation list of a delegate produced from an anonymous function contains a single entry. 委托的确切目标对象和目标方法未指定。The exact target object and target method of the delegate are unspecified. 具体而言,它是未指定的,即委托的目标对象是 nullthis 封闭函数成员的值还是其他某个对象。In particular, it is unspecified whether the target object of the delegate is null, the this value of the enclosing function member, or some other object.

允许 (但不要求) 返回同一个委托实例,但不允许将语义相同的匿名函数(具有相同的) ()转换为同一委托类型。Conversions of semantically identical anonymous functions with the same (possibly empty) set of captured outer variable instances to the same delegate types are permitted (but not required) to return the same delegate instance. 此处使用的术语在语义上是相同的,这意味着在所有情况下,匿名函数的执行将在给定相同参数的情况下生成相同的效果。The term semantically identical is used here to mean that execution of the anonymous functions will, in all cases, produce the same effects given the same arguments. 此规则允许对如下代码进行优化。This rule permits code such as the following to be optimized.

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));
        ...
    }
}

由于两个匿名函数委托具有相同的 (空) 一组捕获的外部变量,并且由于匿名函数在语义上是相同的,因此允许编译器使委托引用相同的目标方法。Since the two anonymous function delegates have the same (empty) set of captured outer variables, and since the anonymous functions are semantically identical, the compiler is permitted to have the delegates refer to the same target method. 的确,允许编译器从两个匿名函数表达式返回完全相同的委托实例。Indeed, the compiler is permitted to return the very same delegate instance from both anonymous function expressions.

对表达式树类型的匿名函数转换的计算Evaluation of anonymous function conversions to expression tree types

将匿名函数转换为表达式树类型会生成表达式树, (表达式树类型) 。Conversion of an anonymous function to an expression tree type produces an expression tree (Expression tree types). 更准确地说,对匿名函数转换的评估导致了表示匿名函数本身的结构的对象结构。More precisely, evaluation of the anonymous function conversion leads to the construction of an object structure that represents the structure of the anonymous function itself. 表达式树的准确结构以及用于创建它的确切过程是定义的实现。The precise structure of the expression tree, as well as the exact process for creating it, are implementation defined.

实现示例Implementation example

本部分介绍了使用其他 c # 构造实现的匿名函数转换的可能实现。This section describes a possible implementation of anonymous function conversions in terms of other C# constructs. 此处所述的实现基于 Microsoft c # 编译器所使用的相同原则,但它并不是强制性实现,也不是唯一可行的方法。The implementation described here is based on the same principles used by the Microsoft C# compiler, but it is by no means a mandated implementation, nor is it the only one possible. 它只是简单地提到了转换为表达式树,因为其确切语义超出了本规范的范围。It only briefly mentions conversions to expression trees, as their exact semantics are outside the scope of this specification.

本部分的其余部分提供了一些代码示例,其中包含具有不同特征的匿名函数。The remainder of this section gives several examples of code that contains anonymous functions with different characteristics. 对于每个示例,提供了对仅使用其他 c # 构造的代码的相应转换。For each example, a corresponding translation to code that uses only other C# constructs is provided. 在这些示例中, D 假定使用标识符表示以下委托类型:In the examples, the identifier D is assumed by represent the following delegate type:

public delegate void D();

匿名函数的最简单形式是捕获无外部变量:The simplest form of an anonymous function is one that captures no outer variables:

class Test
{
    static void F() {
        D d = () => { Console.WriteLine("test"); };
    }
}

这可以转换为引用编译器生成的静态方法(在其中放置匿名函数的代码)的委托实例化:This can be translated to a delegate instantiation that references a compiler generated static method in which the code of the anonymous function is placed:

class Test
{
    static void F() {
        D d = new D(__Method1);
    }

    static void __Method1() {
        Console.WriteLine("test");
    }
}

在下面的示例中,匿名函数引用的实例成员 thisIn the following example, the anonymous function references instance members of this:

class Test
{
    int x;

    void F() {
        D d = () => { Console.WriteLine(x); };
    }
}

这可以转换为包含匿名函数代码的编译器生成的实例方法:This can be translated to a compiler generated instance method containing the code of the anonymous function:

class Test
{
    int x;

    void F() {
        D d = new D(__Method1);
    }

    void __Method1() {
        Console.WriteLine(x);
    }
}

在此示例中,匿名函数捕获本地变量:In this example, the anonymous function captures a local variable:

class Test
{
    void F() {
        int y = 123;
        D d = () => { Console.WriteLine(y); };
    }
}

局部变量的生存期现在必须至少扩展到匿名函数委托的生存期。The lifetime of the local variable must now be extended to at least the lifetime of the anonymous function delegate. 这可以通过将本地变量 "提升" 到编译器生成的类的字段来实现。This can be achieved by "hoisting" the local variable into a field of a compiler generated class. 实例化局部变量 (实例 局部变量) 然后与创建编译器生成的类的实例,并且访问本地变量对应于访问编译器生成的类的实例中的字段。Instantiation of the local variable (Instantiation of local variables) then corresponds to creating an instance of the compiler generated class, and accessing the local variable corresponds to accessing a field in the instance of the compiler generated class. 此外,匿名函数会成为编译器生成的类的实例方法:Furthermore, the anonymous function becomes an instance method of the compiler generated class:

class Test
{
    void F() {
        __Locals1 __locals1 = new __Locals1();
        __locals1.y = 123;
        D d = new D(__locals1.__Method1);
    }

    class __Locals1
    {
        public int y;

        public void __Method1() {
            Console.WriteLine(y);
        }
    }
}

最后,下面的匿名函数捕获和 this 两个具有不同生存期的局部变量:Finally, the following anonymous function captures this as well as two local variables with different lifetimes:

class Test
{
    int x;

    void F() {
        int y = 123;
        for (int i = 0; i < 10; i++) {
            int z = i * 2;
            D d = () => { Console.WriteLine(x + y + z); };
        }
    }
}

在这里,将为每个在其中捕获局部变量的语句块创建一个编译器生成的类,以便不同块中的局部变量可以具有独立生存期。Here, a compiler generated class is created for each statement block in which locals are captured such that the locals in the different blocks can have independent lifetimes. 的实例 __Locals2 (内部语句块的编译器生成的类)包含局部变量 z 和引用实例的字段 __Locals1An instance of __Locals2, the compiler generated class for the inner statement block, contains the local variable z and a field that references an instance of __Locals1. 的一个实例 __Locals1 ,它是外部语句块的编译器生成的类,其中包含局部变量 y 和引用 this 封闭函数成员的字段。An instance of __Locals1, the compiler generated class for the outer statement block, contains the local variable y and a field that references this of the enclosing function member. 利用这些数据结构,可以通过的实例访问所有捕获的外部变量 __Local2 ,匿名函数的代码就可以作为该类的实例方法实现。With these data structures it is possible to reach all captured outer variables through an instance of __Local2, and the code of the anonymous function can thus be implemented as an instance method of that class.

class Test
{
    void F() {
        __Locals1 __locals1 = new __Locals1();
        __locals1.__this = this;
        __locals1.y = 123;
        for (int i = 0; i < 10; i++) {
            __Locals2 __locals2 = new __Locals2();
            __locals2.__locals1 = __locals1;
            __locals2.z = i * 2;
            D d = new D(__locals2.__Method1);
        }
    }

    class __Locals1
    {
        public Test __this;
        public int y;
    }

    class __Locals2
    {
        public __Locals1 __locals1;
        public int z;

        public void __Method1() {
            Console.WriteLine(__locals1.__this.x + __locals1.y + z);
        }
    }
}

在此处用于捕获局部变量的相同技术也可以在将匿名函数转换为表达式树时使用:对编译器生成的对象的引用可以存储在表达式树中,对局部变量的访问可表示为这些对象上的字段访问。The same technique applied here to capture local variables can also be used when converting anonymous functions to expression trees: References to the compiler generated objects can be stored in the expression tree, and access to the local variables can be represented as field accesses on these objects. 此方法的优点是允许在委托和表达式树之间共享 "提升" 的局部变量。The advantage of this approach is that it allows the "lifted" local variables to be shared between delegates and expression trees.

方法组转换Method group conversions

隐式转换 () 存在从方法组 (表达式分类) 到兼容的委托类型的隐转换。An implicit conversion (Implicit conversions) exists from a method group (Expression classifications) to a compatible delegate type. 给定委托类型 DE 归类为方法组的表达式时,存在从到的隐式转换 E D E 。如果包含至少一个适用于其正常窗体的方法 (适用的 函数成员) 到通过使用的参数类型和修饰符构造的参数列表 D ,如下所述。Given a delegate type D and an expression E that is classified as a method group, an implicit conversion exists from E to D if E contains at least one method that is applicable in its normal form (Applicable function member) to an argument list constructed by use of the parameter types and modifiers of D, as described in the following.

以下中描述了从方法组转换为委托类型的编译时应用程序 E DThe compile-time application of a conversion from a method group E to a delegate type D is described in the following. 请注意,是否存在从到的隐式转换, E D 并不保证转换的编译时应用程序不会出错。Note that the existence of an implicit conversion from E to D does not guarantee that the compile-time application of the conversion will succeed without error.

  • M选择与方法调用对应的单个方法 (方法调用) 的方法调用 E(A) ,并进行以下修改:A single method M is selected corresponding to a method invocation (Method invocations) of the form E(A), with the following modifications:
    • 参数列表 A 是表达式的列表,每个表达式都归类为变量,并且具有类型和修饰符 (ref 或的 out formal_parameter_list 中的相应参数) DThe argument list A is a list of expressions, each classified as a variable and with the type and modifier (ref or out) of the corresponding parameter in the formal_parameter_list of D.
    • 考虑的候选方法只是那些适用于其正常形式 (适用的函数成员) ,而不是仅在其展开形式中适用的方法。The candidate methods considered are only those methods that are applicable in their normal form (Applicable function member), not those applicable only in their expanded form.
  • 如果 方法调用 的算法产生错误,则会发生编译时错误。If the algorithm of Method invocations produces an error, then a compile-time error occurs. 否则,该算法将生成单个最佳方法, M 该方法的参数数目与相同 D ,转换被视为存在。Otherwise the algorithm produces a single best method M having the same number of parameters as D and the conversion is considered to exist.
  • 所选方法 M 必须兼容 (委托类型) 委托兼容性 D ,否则将发生编译时错误。The selected method M must be compatible (Delegate compatibility) with the delegate type D, or otherwise, a compile-time error occurs.
  • 如果所选方法 M 是实例方法,则与关联的实例表达式 E 确定委托的目标对象。If the selected method M is an instance method, the instance expression associated with E determines the target object of the delegate.
  • 如果所选方法 M 是通过实例表达式上的成员访问方式表示的扩展方法,则该实例表达式将确定委托的目标对象。If the selected method M is an extension method which is denoted by means of a member access on an instance expression, that instance expression determines the target object of the delegate.
  • 转换结果为类型的值  D ,即引用所选方法和目标对象的新创建的委托。The result of the conversion is a value of type D, namely a newly created delegate that refers to the selected method and target object.
  • 请注意,如果 方法调用 的算法找不到实例方法,但在将调用 E(A) 作为扩展方法调用处理 (扩展 方法调用) ,则此过程可能会导致创建扩展方法的委托。Note that this process can lead to the creation of a delegate to an extension method, if the algorithm of Method invocations fails to find an instance method but succeeds in processing the invocation of E(A) as an extension method invocation (Extension method invocations). 因此,创建的委托将捕获扩展方法以及其第一个参数。A delegate thus created captures the extension method as well as its first argument.

下面的示例演示方法组转换:The following example demonstrates method group conversions:

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 为类型的值 D1The assignment to d1 implicitly converts the method group F to a value of type D1.

的赋值 d2 演示了如何创建一个委托,该委托指向 (逆变) 参数类型和派生程度更高的 (协变) 返回类型的派生方法。The assignment to d2 shows how it is possible to create a delegate to a method that has less derived (contravariant) parameter types and a more derived (covariant) return type.

d3如果该方法不适用,则分配为显示不存在转换的情况。The assignment to d3 shows how no conversion exists if the method is not applicable.

用于 d4 显示此方法必须以其正常形式适用的方式的赋值。The assignment to d4 shows how the method must be applicable in its normal form.

用于 d5 显示如何允许委托和方法的参数和返回类型不同于引用类型的赋值。The assignment to d5 shows how parameter and return types of the delegate and method are allowed to differ only for reference types.

与所有其他隐式和显式转换一样,转换运算符可用于显式执行方法组转换。As with all other implicit and explicit conversions, the cast operator can be used to explicitly perform a method group conversion. 因此,示例Thus, the example

object obj = new EventHandler(myDialog.OkClick);

可以改为写入could instead be written

object obj = (EventHandler)myDialog.OkClick;

方法组可能会影响重载决策,并参与类型推理。Method groups may influence overload resolution, and participate in type inference. 有关更多详细信息,请参阅 函数成员See Function members for further details.

方法组转换的运行时计算如下所示:The run-time evaluation of a method group conversion proceeds as follows:

  • 如果在编译时选择的方法是实例方法,或者它是作为实例方法访问的扩展方法,则将从与关联的实例表达式确定委托的目标对象 EIf the method selected at compile-time is an instance method, or it is an extension method which is accessed as an instance method, the target object of the delegate is determined from the instance expression associated with E:
    • 计算实例表达式。The instance expression is evaluated. 如果此计算导致异常,则不执行其他步骤。If this evaluation causes an exception, no further steps are executed.
    • 如果实例表达式为 reference_type,则由实例表达式计算的值将成为目标对象。If the instance expression is of a reference_type, the value computed by the instance expression becomes the target object. 如果所选的方法是实例方法,并且目标对象为 nullSystem.NullReferenceException 则会引发,而不执行进一步的步骤。If the selected method is an instance method and the target object is null, a System.NullReferenceException is thrown and no further steps are executed.
    • 如果实例表达式为 value_type,则会执行装箱运算 (装箱转换 ,) 将值转换为对象,此对象将成为目标对象。If the instance expression is of a value_type, a boxing operation (Boxing conversions) is performed to convert the value to an object, and this object becomes the target object.
  • 否则,所选方法为静态方法调用的一部分,并且委托的目标对象是 nullOtherwise the selected method is part of a static method call, and the target object of the delegate is null.
  • 分配委托类型的新实例 DA new instance of the delegate type D is allocated. 如果没有足够的内存可用于分配新的实例, System.OutOfMemoryException 则会引发,而不执行进一步的步骤。If there is not enough memory available to allocate the new instance, a System.OutOfMemoryException is thrown and no further steps are executed.
  • 使用对在编译时确定的方法以及对上面计算的目标对象的引用来初始化新的委托实例。The new delegate instance is initialized with a reference to the method that was determined at compile-time and a reference to the target object computed above.