Use IL to implement type conversion and il to implement type conversion
In my previous articles, I have roughly introduced some implicit and explicit type conversion rules between types. However, CSharp Language Specification was not carefully studied at the time, so the implementation was incomplete. In addition, it only partially solves whether type conversion can be performed between types, but still does not solve how to perform type conversion, especially when defining generic types, we clearly know what type the parameter of the generic type is, but we cannot directly convert the type:
If (typeof (T) = typeof (int) {int intValue = (int) value; // error: the type "T" cannot be converted to "int "}
Onlyobject
Type "transit:
if (typeof(T) == typeof(int)) { int intValue = (int)(object)value;}
Here, the value-Type Packing/unpacking operations are used to avoid errors. But what if I want to be more general? For example, I knowchar
Type can be implicitly convertedint
Type, so can I write it like this:
if (typeof(T) == typeof(int) || typeof(T) == typeof(char)) { int intValue = (int)(object)value;}
Unfortunately, ifvalue
Yeschar
Type, the System. InvalidCastException: the specified conversion is invalid. Different types must be written separately. This is because most of the IL code for type conversion is completely determined during the compilation period. At runtime, only compatible reference type conversion (CastClass) and packing/unpacking (Box/Unbox) can be performed) conversion.
To enhance and simplify the type conversion during running, I carefully studied CSharp Language Specification and IL, and used System. reflection. emit implements a framework for dynamically generating IL for type conversion at runtime, which supports conversion of similar types as the compiler at runtime, and provides complete support for generic types. For example, the following section converts any numeric typeulong
:
// Assume that the TValue must be of the numerical type. Public ulong ToUInt64 <TValue> (TValue value) {return Convert. ChangeType <TValue, ulong> (value );}
The main interface for type conversion is the Convert class, which can be fully compatibleVarious numeric type conversion, implicit/explicit reference type conversion, and user-defined type conversion, Including:
- Get type converter:
GetConverter<TInput, TOutput>()
AndGetConverter(Type inputType, Type outputType)
The resulting Converter <TInput, TOutput> delegate can be directly used for type conversion.
- Directly convert the Data Type:
ChangeType<TInput, TOutput>(TInput value)
,ChangeType<TOutput>(object value)
AndChangeType(object value, Type outputType)
.
- Determine whether type conversion can be performed:
CanChangeType(Type inputType, Type outputType)
.
- How to add a type conversion method during running:
AddConverter<TInput, TOutput>(Converter<TInput, TOutput> converter)
AndAddConverterProvider(IConverterProvider provider)
.
All type conversions are implemented by using System. Reflection. Emit to dynamically generate IL, ensuring the efficiency of type conversion. Therefore, the extension method EmitConversion of the ILGenerator class is also provided, and the type conversion can be performed when the IL code is generated.
All the above Code can be found in the Cyjb. Conversions and Cyjb. Reflection namespaces.
Next, I will briefly introduce how to use IL to implement type conversion.
I. predefined type conversion
According to CSharp Language Specification, pre-defined type conversion mainly includes: logo conversion, implicit numeric conversion, implicit enumeration conversion, and Nullable <T>) such as implicit conversion, implicit reference conversion, packing conversion, explicit numeric conversion, explicit enumeration conversion, Explicit conversions of the null type, explicit conversion of the explicit reference and box-breaking conversion. Byimplicit
Andexplicit
User-Defined type conversion for keyword declaration will be introduced in the next section.
The process of these types of conversions is provided in the Specification, but the efficiency is very low if you simply judge these types of conversions in order. Therefore, I use the algorithm shown in the following code to make a judgment:
Figure 1 pre-defined type conversion judgment Algorithm
The IL commands used for pre-defined type conversion are generally relatively simple, basicallycastclass
,box
Andunbox
Commands, which are more complex than implicit/Explicit conversions and empty conversions.
I have summarized the following table for implicit/explicit numeric conversion, and its implementation is basically the process of Table query. At the top of the table is the IL command for no overflow check, and the IL command for overflow check. Spaces indicate that the type conversion can be performed without inserting the IL command. The green background indicates implicit value conversion, the yellow background indicates explicit Numerical Conversion:
Figure 2 implicit/explicit numeric Conversion
Note:There is a distinction between overflow checks (checked/unchecked) for Numeric conversions, and the Decimal type is not listed in the table, the conversion between the Decimal type and other numeric types relies on the type conversion method defined by implicit/explicit, which is not suitable for table search.
Can be empty type conversion, can be divided into three situations (SetS
,T
All are non-empty value types ):
For information on possible empty conversions, see BetweenNullableConversion. cs, FromNullableConversion. cs, and ToNullableConversion. cs.
Ii. User-Defined type conversion
This refersimplicit
Andexplicit
Custom type conversion method for keyword declaration. The algorithm introduced below comes from CSharp Language Specification 6.4.5 User-defined explicit conversions. I will not distinguish between implicit type conversion and explicit type conversion, this distinction is not important at runtime.
First, we need to clarify some concepts.
Increase conversion Operators: If there is a null value typeS
To non-null typeT
The user-defined type conversion operatorS?
ConvertT?
. This lifting conversion operator is executed fromS?
ToS
The next step isS
ToT
User-Defined type conversion, and then fromT
ToT?
If it isS?
Isnull
, Then directly convert to the valuenull
OfT?
.
Include/be included: IfA
Type can be implicitly converted (meaning pre-defined type conversion)B
Type, andA
AndB
It is calledA
QuiltB
Include, AndB
Include A
.
Maximum inclusion: In a given type set,Maximum inclusionCan contain all other types in the set. If no type can contain all other types in the Set, there is no type with the highest degree of inclusion. More intuitively, the most inclusive type is the most "extensive" type in the Set-other types can be implicitly converted to it.
Maximum degree of inclusion: In a given type set,Maximum degree of inclusionCan be included by all other types in the set. If no type can be included by all other types in the Set, no type with the highest degree of inclusion exists. More intuitively, the most contained type is the most "precise" type in the Set-it can be implicitly converted to other types.
SlaveS
TypeT
Type User-defined explicit type conversion is handled as follows:
For more information about this algorithm, see UserConversionCache. cs.
Iii. Additional user-defined type conversion
The two methods described above are all fully determined type conversion methods during compilation. The Convert class provides two additional interfaces, which can provide any type conversion method.
AddConverter<TInput, TOutput>(Converter<TInput, TOutput> converter)
Methods can register any type of conversion method, andAddConverterProvider(IConverterProvider provider)
The method can register the provider of the type conversion method, and batch provide the type conversion method related to a certain type (for example, refer to StringConverterProvider. cs, and provide the type conversion method related to the string ).
Note:: The highest priority is the pre-defined type conversion method and user-defined type conversion method, followedAddConverter
Method registration type conversion method, and thenIConverterProvider
OfGetConverterTo
The provided type conversion method isIConverterProvider
OfGetConverterFrom
Provides the type conversion method, and the latter has a higher priority.
The complete source code of the content mentioned in this article can be found in Cyjb. Conversions and Cyjb. Reflection.