【问题标题】:Strange type conversion on c# Expressionsc# 表达式上的奇怪类型转换
【发布时间】:2021-08-25 10:53:42
【问题描述】:

让我们有一个简单的类

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 转换为字节比将左侧转换为 int 更合理。
在其余情况下,左侧和右侧是字节
我的问题是为什么应用这些转换?

【问题讨论】:

  • 快速测试表明,任何小于 int 的东西都会发生这种情况:sharplab.io/…

标签: c# lambda expression


【解决方案1】:

我们来看看the spec:

整数比较运算符

预定义的整数比较运算符有:

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

这些运算符中的每一个都比较两个整数操作数的数值并返回一个布尔值,指示特定关系是真还是假。

如您所见,我们实际上并没有比较两个 bytes 或两个 sbytescharsshortsushorts== 运算符。

但是,存在从这些类型到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;

【讨论】:

  • 我注意到对于实际代码(不是表达式),使用了一个简单的ceq
  • 我知道你无法回答这个问题,但“为什么”会真的很有趣...
  • 因为对于现代 CPU 来说,使用整数类型而不是字节更好
  • @Selvin 不,这是因为没有用于byte 比较的 IL 指令,IL 只是在加载时扩展为 int,并在存储时截断
  • 这是因为没有字节的IL指令 ... 为什么?根本原因是什么?
【解决方案2】:

以 canton7 的回答为基础。

为什么bytes(和其他类型)特别转换为整数的原因是为了语言互操作性,确保默认算术行为在所有实现。

出于特定原因,我们必须参考公共语言基础设施标准 (ECMA-335),该标准控制运行时和编译器应如何处理类型,无论是分别在评估堆栈还是验证堆栈上。

ECMA-335 §III.1.1

虽然 [Common Type System] 定义了丰富的类型系统,而 [Common Language Specification] 指定了一个子集,可用于 语言互操作性,[通用语言基础设施]本身处理一组更简单的类型。

ECMA-335 §III.1.1.1

[Common Language Infrastructure] 仅对数字类型 int32(4 字节有符号整数)、int64 进行操作 (8 字节有符号整数)、native int (native-size integers) 和 F (native-size 浮点数)。

ECMA-335 §III.1.1.1.2 的注释

短(即 1 字节和 2 字节)整数在所有架构上都加载为 4 字节数字 并且这些 4 字节数字始终被跟踪为与 8 字节数字不同。这有助于 通过确保默认算术行为在所有实现上具有相同的结果来实现代码的可移植性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-14
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2014-10-23
    • 2011-10-07
    • 2014-02-02
    相关资源
    最近更新 更多