c# 表达式上的奇怪类型转换
Strange type conversion on c# Expressions
来个简单的class
class User
{
public byte IdByte { get; set; }
}
所以当我写这样的表达式时:
Expression<Func<User, bool>> expression1 = x => x.IdByte == 3;
Expression<Func<User, bool>> expression2 = x => x.IdByte == (byte)3;
byte b = 3;
Expression<Func<User, bool>> expression3 = x => x.IdByte == b;
Expression<Func<User, bool>> expression4 = x => x.IdByte == byte.MaxValue;
并观察表达式调试视图,我看到还有一个类型转换为 System.Int32 类型:
//expression1 ------> x => (Convert(x.IdByte, Int32) == 3)
//expression2 ------> x => (Convert(x.IdByte, Int32) == 3)
//expression3 ------> x => (Convert(x.IdByte, Int32) == Convert(value(....c__DisplayClass1_0).b, Int32))
//expression4 ------> x => (Convert(x.IdByte, Int32) == 255)
在第一个和第二个表达式中,将右侧 - 3 转换为 byte 比将左侧转换为 int 更合理。
在其余情况下,左侧和右侧是字节
我的问题是为什么应用这些转换?
让我们检查一下 the spec:
Integer comparison operators
The predefined integer comparison operators are:
bool operator ==(int x, int y);
bool operator ==(uint x, uint y);
bool operator ==(long x, long y);
bool operator ==(ulong x, ulong y);
bool operator !=(int x, int y);
bool operator !=(uint x, uint y);
bool operator !=(long x, long y);
bool operator !=(ulong x, ulong y);
bool operator <(int x, int y);
bool operator <(uint x, uint y);
bool operator <(long x, long y);
bool operator <(ulong x, ulong y);
bool operator >(int x, int y);
bool operator >(uint x, uint y);
bool operator >(long x, long y);
bool operator >(ulong x, ulong y);
bool operator <=(int x, int y);
bool operator <=(uint x, uint y);
bool operator <=(long x, long y);
bool operator <=(ulong x, ulong y);
bool operator >=(int x, int y);
bool operator >=(uint x, uint y);
bool operator >=(long x, long y);
bool operator >=(ulong x, ulong y);
Each of these operators compares the numeric values of the two integer operands and returns a bool value that indicates whether the particular relation is true or false.
如您所见,我们实际上并没有比较两个 bytes
或两个 sbytes
、chars
、[=18= 的 ==
运算符], 或 ushorts
.
但是,存在从这些类型到 int
的隐式转换,因此编译器将这些转换应用于双方,并使用 bool ==(int x, int y)
.
如果你写:
byte val = 3;
bool b = val == (byte)4;
编译器会有效地将其转换为:
byte val = 3;
bool b = (int)val == (int)(byte)4;
(int)(byte)4
转换当然是完全没有必要的:编译器只会将其转换为整数文字。
byte val = 3;
bool b = (int)val == 4;
以 canton7 的答案为基础。
为什么 bytes
(和其他类型)特别转换为整数的原因是 语言互操作性 通过确保默认算术行为将在所有实现上产生相同的结果。
出于特定原因,我们必须参考控制 如何 运行时和编译器应如何处理类型的公共语言基础设施标准 (ECMA-335),无论是在评估中或验证堆栈。
ECMA-335 §III.1.1
While the [Common Type System] defines a rich type system and the [Common Language Specification] specifies a subset that can be used for
language interoperability, the [Common Language Infrastructure] itself deals with a much simpler set of types.
ECMA-335 §III.1.1.1
The [Common Language Infrastructure] only operates on the numeric types int32 (4-byte signed integers), int64
(8-byte signed integers), native int (native-size integers), and F (native-size
floating-point numbers).
ECMA-335 §III.1.1.1.2 的注释
Short (i.e., 1- and 2-byte) integers are loaded as 4-byte numbers on all architectures
and these 4-byte numbers are always tracked as distinct from 8-byte numbers. This helps
portability of code by ensuring that the default arithmetic behavior will have identical results on all implementations.
来个简单的class
class User
{
public byte IdByte { get; set; }
}
所以当我写这样的表达式时:
Expression<Func<User, bool>> expression1 = x => x.IdByte == 3;
Expression<Func<User, bool>> expression2 = x => x.IdByte == (byte)3;
byte b = 3;
Expression<Func<User, bool>> expression3 = x => x.IdByte == b;
Expression<Func<User, bool>> expression4 = x => x.IdByte == byte.MaxValue;
并观察表达式调试视图,我看到还有一个类型转换为 System.Int32 类型:
//expression1 ------> x => (Convert(x.IdByte, Int32) == 3)
//expression2 ------> x => (Convert(x.IdByte, Int32) == 3)
//expression3 ------> x => (Convert(x.IdByte, Int32) == Convert(value(....c__DisplayClass1_0).b, Int32))
//expression4 ------> x => (Convert(x.IdByte, Int32) == 255)
在第一个和第二个表达式中,将右侧 - 3 转换为 byte 比将左侧转换为 int 更合理。
在其余情况下,左侧和右侧是字节
我的问题是为什么应用这些转换?
让我们检查一下 the spec:
Integer comparison operators
The predefined integer comparison operators are:
bool operator ==(int x, int y); bool operator ==(uint x, uint y); bool operator ==(long x, long y); bool operator ==(ulong x, ulong y); bool operator !=(int x, int y); bool operator !=(uint x, uint y); bool operator !=(long x, long y); bool operator !=(ulong x, ulong y); bool operator <(int x, int y); bool operator <(uint x, uint y); bool operator <(long x, long y); bool operator <(ulong x, ulong y); bool operator >(int x, int y); bool operator >(uint x, uint y); bool operator >(long x, long y); bool operator >(ulong x, ulong y); bool operator <=(int x, int y); bool operator <=(uint x, uint y); bool operator <=(long x, long y); bool operator <=(ulong x, ulong y); bool operator >=(int x, int y); bool operator >=(uint x, uint y); bool operator >=(long x, long y); bool operator >=(ulong x, ulong y);
Each of these operators compares the numeric values of the two integer operands and returns a bool value that indicates whether the particular relation is true or false.
如您所见,我们实际上并没有比较两个 bytes
或两个 sbytes
、chars
、[=18= 的 ==
运算符], 或 ushorts
.
但是,存在从这些类型到 int
的隐式转换,因此编译器将这些转换应用于双方,并使用 bool ==(int x, int y)
.
如果你写:
byte val = 3;
bool b = val == (byte)4;
编译器会有效地将其转换为:
byte val = 3;
bool b = (int)val == (int)(byte)4;
(int)(byte)4
转换当然是完全没有必要的:编译器只会将其转换为整数文字。
byte val = 3;
bool b = (int)val == 4;
以 canton7 的答案为基础。
为什么 bytes
(和其他类型)特别转换为整数的原因是 语言互操作性 通过确保默认算术行为将在所有实现上产生相同的结果。
出于特定原因,我们必须参考控制 如何 运行时和编译器应如何处理类型的公共语言基础设施标准 (ECMA-335),无论是在评估中或验证堆栈。
ECMA-335 §III.1.1
While the [Common Type System] defines a rich type system and the [Common Language Specification] specifies a subset that can be used for language interoperability, the [Common Language Infrastructure] itself deals with a much simpler set of types.
ECMA-335 §III.1.1.1
The [Common Language Infrastructure] only operates on the numeric types int32 (4-byte signed integers), int64 (8-byte signed integers), native int (native-size integers), and F (native-size floating-point numbers).
ECMA-335 §III.1.1.1.2 的注释
Short (i.e., 1- and 2-byte) integers are loaded as 4-byte numbers on all architectures and these 4-byte numbers are always tracked as distinct from 8-byte numbers. This helps portability of code by ensuring that the default arithmetic behavior will have identical results on all implementations.