转换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. 转换可以是隐式的或显式的,这确定是否需要显式强制转换。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
  • 隐式可为 null 转换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.

出于转换目的,objectdynamic 类型被视为等效类型。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.

  • 因为 objectdynamic 被视为等效的,所以在 objectdynamic之间存在标识转换,并且在用 dynamic 替换所有匹配项时,构造类型之间是相同的。Because 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:

  • sbyteshortintlongfloatdoubledecimalFrom sbyte to short, int, long, float, double, or decimal.
  • byteshortushortintuintlongulongfloatdoubledecimalFrom byte to short, ushort, int, uint, long, ulong, float, double, or decimal.
  • shortintlongfloatdoubledecimalFrom short to int, long, float, double, or decimal.
  • ushortintuintlongulongfloatdoubledecimalFrom ushort to int, uint, long, ulong, float, double, or decimal.
  • intlongfloatdoubledecimalFrom int to long, float, double, or decimal.
  • uintlongulongfloatdoubledecimalFrom uint to long, ulong, float, double, or decimal.
  • longfloatdoubledecimalFrom long to float, double, or decimal.
  • ulongfloatdoubledecimalFrom ulong to float, double, or decimal.
  • charushortintuintlongulongfloatdoubledecimalFrom char to ushort, int, uint, long, ulong, float, double, or decimal.
  • floatdoubleFrom float to double.

intuintlongulongfloat 以及从 longulongdouble 的转换可能会导致精度损失,但永远不会导致数量级损失。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.IFormattableSystem.FormattableString (实现 System.IFormattable)。An 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.

隐式可为 null 转换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?.
  • ST?的隐式转换。An implicit conversion from S to T?.

基于从 ST 的基础转换计算隐式可为 null 的转换,如下所示: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),则结果为类型 T?的 null 值。If the source value is null (HasValue property is false), the result is the null value of type T?.
    • 否则,转换将作为从 S?S的解包进行计算,后跟从 ST的底层转换,后跟从 TT?的包装(可为 null 的类型)。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?,则会将转换计算为从 ST 后跟从 TT?的基础转换。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 T,提供的 S 派生自 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 T,提供的 S 派生自 TFrom any interface_type S to any interface_type T, provided S is derived from T.
  • 在元素类型为的array_type SSE 到元素类型 Tarray_type 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_types.
    • 存在从 SETE的隐式引用转换。An implicit reference conversion exists from SE to TE.
  • 从任何array_typeSystem.Array 及其实现的接口。From any array_type to System.Array and the interfaces it implements.
  • 从一维数组类型 S[]System.Collections.Generic.IList<T> 及其基接口,前提是存在从 ST的隐式标识或引用转换。From 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_typeSystem.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_type T 如果它具有隐式标识或到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 可转换为 T的变体(差异转换)。From 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_types 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. 存在从任何non_nullable_value_typeobjectdynamic的装箱转换,以 System.ValueTypeinterface_type实现的任何non_nullable_value_typeA 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 具有到 I的标识转换。A 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 I0I0 区分方差(变化转换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 到任何类型 T的表达式的隐式动态转换。An 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

s2i 的分配都采用隐式动态转换,在这种情况下,将在运行时暂停操作的绑定。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 但不能 int的转换。A conversion is found to string but not to int.

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

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

  • 如果 short 的值在目标类型的范围内,则可以将 int 类型的constant_expression常数表达式)转换为类型 sbytebyteushortuintulong或 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 类型的constant_expression可转换为类型 ulong,前提是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

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

  • T 到其有效的基类 C,从 TC的任何基类,从 TC实现的任何接口。From 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 到接口类型 IT的有效接口集中,并从 TI的任何基接口。From 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 到类型形参 U,提供 T 取决于 U类型形参约束)。From T to a type parameter U, provided T depends on U (Type parameter constraints). 在运行时,如果 U 是值类型,则 TU 都必须是相同的类型,并且不执行任何转换。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 具有到 S的标识转换。From 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. 在运行时,转换的执行方式与转换到 S0的方式相同。At 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:

  • sbytebyteushortuintulongcharFrom sbyte to byte, ushort, uint, ulong, or char.
  • bytesbytecharFrom byte to sbyte and char.
  • shortsbytebyteushortuintulongcharFrom short to sbyte, byte, ushort, uint, ulong, or char.
  • ushortsbytebyteshortcharFrom ushort to sbyte, byte, short, or char.
  • intsbytebyteshortushortuintulongcharFrom int to sbyte, byte, short, ushort, uint, ulong, or char.
  • uintsbytebyteshortushortintcharFrom uint to sbyte, byte, short, ushort, int, or char.
  • longsbytebyteshortushortintuintulongcharFrom long to sbyte, byte, short, ushort, int, uint, ulong, or char.
  • ulongsbytebyteshortushortintuintlongcharFrom ulong to sbyte, byte, short, ushort, int, uint, long, or char.
  • charsbytebyteshortFrom char to sbyte, byte, or short.
  • floatsbytebyteshortushortintuintlongulongchardecimalFrom float to sbyte, byte, short, ushort, int, uint, long, ulong, char, or decimal.
  • doublesbytebyteshortushortintuintlongulongcharfloatdecimalFrom double to sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or decimal.
  • decimalsbytebyteshortushortintuintlongulongcharfloatdoubleFrom 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.OverflowExceptionIn 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.OverflowExceptionIf 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.OverflowExceptionIf 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.
  • 对于从 doublefloat的转换,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.
  • 对于从 floatdoubledecimal的转换,将源值转换为 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、无限大或太大而无法表示为 decimal,则会引发 System.OverflowExceptionIf the source value is NaN, infinity, or too large to represent as a decimal, a System.OverflowException is thrown.
  • 对于从 decimalfloatdouble的转换,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:

  • sbytebyteshortushortintuintlongulongcharfloatdoubledecimal 到任何enum_typeFrom sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, or decimal to any enum_type.
  • 从任何enum_typesbytebyteshortushortintuintlongulongcharfloatdoubledecimalFrom 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. 例如,给定enum_type E,以及基础类型 int,则 Ebyteint 的显式数字转换(从 bytebyte 的显式数字转换处理),并将从 Ebyte 的转换处理为从 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.

显式可为 null 的转换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?.
  • ST?的显式转换。An explicit conversion from S to T?.
  • S?T的显式转换。An explicit conversion from S? to T.

基于从 ST 的基础转换计算可以为 null 的转换,如下所示: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),则结果为类型 T?的 null 值。If the source value is null (HasValue property is false), the result is the null value of type T?.
    • 否则,转换将作为从 S?S的解包进行计算,后跟从 ST的底层转换,然后是从 TT?的包装。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?,则会将转换计算为从 ST 后跟从 TT?的基础转换。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 的解包,后跟从 ST的基础转换。If 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,则尝试解包的值会引发异常。Note 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,提供的 ST的基类。From 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 不是从 T派生的。From any interface_type S to any interface_type T, provided S is not derived from T.
  • 在元素类型为的array_type SSE 到元素类型 Tarray_type 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_types.
    • 存在从 SETE的显式引用转换。An 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> 及其基接口,前提是存在从 ST的显式引用转换。From 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[],前提是有显式标识或从 ST的引用转换。From 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 具有 T的标识转换。From 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 可以转换为 TT 变体转换为 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,而对于每个类型参数 D,则以下保留:From 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 固定,则 SiTi相同。If Xi is invariant, then Si is identical to Ti.
    • 如果 Xi 是协变的,则存在从 SiTi的隐式或显式标识或引用转换。If 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.InvalidCastExceptionIf 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. 从类型 objectdynamicSystem.ValueType 到任何non_nullable_value_type,以及从任何interface_type到实现non_nullable_value_type的任意interface_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 具有到 I的标识转换。A 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 可以转换为 II 可转换为 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 类型的表达式到任何类型 T的显式动态转换。An 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

在编译时可以找到 oC 的最佳转换,以便成为显式引用转换。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 -- stringC 的运行时类型进行了定义的转换,并成功了。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

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

  • 从有效的基类 CTT 和从 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 到任何interface_type I 如果尚未从 TI的隐式转换。From 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 是值类型,则 TU 都必须是相同的类型,并且不执行任何转换。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 到类型 A,都存在标准的显式转换。In 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 分别等于 STFor 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 转换为目标类型 TA 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_type的。Neither S0 nor T0 is an interface_type.
  • 如果不包括用户定义的转换,则从 ST 或从 TS的转换不存在。Excluding 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,然后执行从 ST 的用户定义的转换,后跟从 TT?的包装,只不过 null 值 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 的转换永远不会首先执行从 SX 的用户定义的转换,然后执行从 XT的用户定义的转换。In 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存在标准隐式转换(标准隐式转换),并且 AB 都不interface_type,则 A 被视为包含BB 称为包含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_types, 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:

  • 确定 S0T0的类型。Determine the types S0 and T0. 如果 ST 是可以为 null 的类型,S0T0 是其基础类型,则 S0T0 分别等于 STIf 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 的类型转换为 T包含的类型。This 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.
  • 查找 U中运算符的最特定的源类型 SXFind 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.
    • 否则,SXU中运算符的组合源类型集中最常包含的类型。Otherwise, 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.
  • 查找 U中运算符的最特定目标类型 TXFind 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.
    • 否则,TXU中运算符的组合目标类型集中最包含的类型。Otherwise, 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:
    • 如果未 SX``S,则执行从 SSX 的标准隐式转换。If S is not SX, then a standard implicit conversion from S to SX is performed.
    • 调用最特定的转换运算符,以将 SX 转换为 TXThe most specific conversion operator is invoked to convert from SX to TX.
    • 如果未 T``TX,则执行从 TXT 的标准隐式转换。If 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:

  • 确定 S0T0的类型。Determine the types S0 and T0. 如果 ST 是可以为 null 的类型,S0T0 是其基础类型,则 S0T0 分别等于 STIf 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 是类或结构)以及 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 转换为包含 T包含或包含的类型。This 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.
  • 查找 U中运算符的最特定的源类型 SXFind 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.
    • 否则,SXU中运算符的组合源类型集中的最包含的类型。Otherwise, 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.
  • 查找 U中运算符的最特定目标类型 TXFind 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.
    • 否则,TXU中运算符的组合目标类型集中最常被包含的类型。Otherwise, 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:
    • 如果未 SX``S,则执行从 SSX 的标准显式转换。If S is not SX, then a standard explicit conversion from S to SX is performed.
    • 调用最特定的用户定义转换运算符,以将 SX 转换为 TXThe most specific user-defined conversion operator is invoked to convert from SX to TX.
    • 如果未 T``TX,则执行从 TXT 的标准显式转换。If 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 的匿名函数与 D 提供的委托类型兼容:Specifically, 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 的参数具有 out 参数修饰符。If 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 中的每个参数都具有与 F中的相应参数相同的类型和修饰符。If 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 的主体将被视为statement_expressionexpression 语句 )。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 返回类型 TF 为 async 并且 D 具有返回类型 Task<T>,则当 F 的每个参数都给定 D中对应参数的类型时,F 的主体是可隐式转换为 T的有效表达式(wrt表达式)。If 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 返回类型 TF 为 async 并且 D 具有返回类型 Task<T>,则当 F 的每个参数都给定 D中对应参数的类型时,F 的主体为有效语句块(wrt),其中每个 return 语句指定一个可隐式转换为 T的表达式。If 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).

如果 FD的委托类型兼容,则 lambda 表达式 F 与表达式树类型兼容 Expression<D>A 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 类型的参数并返回 R类型的值:The 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 提供类型 int时,x+1 是可隐式转换为类型 int的有效表达式。The 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)的结果可隐式转换为类型 doubleLikewise, 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.

但是,第三个赋值是编译时错误,因为当 double``x 给定类型时,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)的结果可隐式转换为任务类型 Task<int>的结果类型 intThe 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. 具体而言,它不指定是 null委托的目标对象、封闭函数成员的 this 值还是其他某个对象。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");
    }
}

在下面的示例中,匿名函数引用 this的实例成员:In 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 和引用 __Locals1的实例的字段。An 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. 给定委托类型 D 和归类为方法组的表达式 ED E,如果 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. 请注意,是否存在从 ED 的隐式转换,并不保证转换的编译时应用程序将成功且不出错。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.

  • 选择与窗体 E(A)的方法调用(方法调用)相对应 M 单个方法,并进行以下修改:A single method M is selected corresponding to a method invocation (Method invocations) of the form E(A), with the following modifications:
    • 参数列表 A 是一个表达式列表,每个表达式都分类为一个变量,并且具有 Dformal_parameter_list中相应参数的类型和修饰符(refout)。The 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. 否则,算法将生成单个最佳方法,MD 具有相同数量的参数,并将转换视为存在。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 隐式转换为类型 D1的值。The 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:

  • 如果在编译时选择的方法是实例方法,或者它是作为实例方法访问的扩展方法,则将从与 E关联的实例表达式确定委托的目标对象:If 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. 如果所选的方法是实例方法,并且目标对象是 null的,则会引发 System.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.
  • 否则,所选方法为静态方法调用的一部分,并且 null委托的目标对象。Otherwise the selected method is part of a static method call, and the target object of the delegate is null.
  • 分配 D 委托类型的新实例。A 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.