我在默认的C#编译器VS 2017 RC Enterprise中注意到了此行为
将double / float强制转换为自身时,将发出Conv.R8 / Conv.R4类型。
但是,如果强制转换为对象或非浮点类型,则不会发生任何事情。
以下示例已在release
模式下编译。在debug
处,IL相似。
示例C#代码:
private double _Double;
private float _Float;
private int _Int;
private object _Object;
private int IntConvertToInt()
{
int x = (int)_Int; //
return x;
}
private int IntAssignToInt()
{
int x = _Int;
return x;
}
private float FloatConvertToFloat()
{
float x = (float)_Float; //Additional OpCode
return x;
}
private float FloatAssignToFloat()
{
float x = _Float;
return x;
}
private double DoubleConvertToDouble()
{
double x = (double)_Double; //Additional OpCode
return x;
}
private double DoubleAssignToDouble()
{
double x = _Double;
return x;
}
private Object ObjectConvertToObject()
{
Object x = (Object)_Object;
return x;
}
private Object ObjectAssignToObject()
{
Object x = _Object;
return x;
}
对应的Il:
.method private hidebysig
instance int32 IntConvertToInt () cil managed
{
// Method begins at RVA 0x2052
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld int32 ConversionTest.Program::_Int
IL_0006: ret
} // end of method Program::IntConvertToInt
.method private hidebysig
instance int32 IntAssignToInt () cil managed
{
// Method begins at RVA 0x2052
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld int32 ConversionTest.Program::_Int
IL_0006: ret
} // end of method Program::IntAssignToInt
.method private hidebysig
instance float32 FloatConvertToFloat () cil managed
{
// Method begins at RVA 0x205a
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld float32 ConversionTest.Program::_Float
IL_0006: conv.r4 //here
IL_0007: ret
} // end of method Program::FloatConvertToFloat
.method private hidebysig
instance float32 FloatAssignToFloat () cil managed
{
// Method begins at RVA 0x2063
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld float32 ConversionTest.Program::_Float
IL_0006: ret
} // end of method Program::FloatAssignToFloat
.method private hidebysig
instance float64 DoubleConvertToDouble () cil managed
{
// Method begins at RVA 0x206b
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld float64 ConversionTest.Program::_Double
IL_0006: conv.r8 //here
IL_0007: ret
} // end of method Program::DoubleConvertToDouble
.method private hidebysig
instance float64 DoubleAssignToDouble () cil managed
{
// Method begins at RVA 0x2074
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld float64 ConversionTest.Program::_Double
IL_0006: ret
} // end of method Program::DoubleAssignToDouble
.method private hidebysig
instance object ObjectConvertToObject () cil managed
{
// Method begins at RVA 0x207c
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld object ConversionTest.Program::_Object
IL_0006: ret
} // end of method Program::ObjectConvertToObject
.method private hidebysig
instance object ObjectAssignToObject () cil managed
{
// Method begins at RVA 0x207c
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld object ConversionTest.Program::_Object
IL_0006: ret
}
为什么Conv.R4 / Conv.R8表现为这种方式?
这个操作码是否很重要,还是可以安全删除/删除?
最佳答案
此link解释了实际情况。引用最重要的部分:
CLI规范在第12.1.3节中规定了在存储位置中使用的浮点数,float和double的精确精度。但是,当在其他位置(如执行堆栈,参数返回值等)中使用浮点数时,它允许超出精度。使用哪种精度留给运行时和基础硬件。这种额外的精度可能导致不同机器或运行时之间浮点评估的细微差异。
这是额外的conv.r4
和conv.r8
指令所在的位置。通常,它们用于将非浮点值强制转换为浮点值。尽管它们的副作用之一是结果值将具有该类型指定的精确精度。这意味着当应用于评估堆栈上的浮点值时,它将截断到指定的精度。
因此,回答您的特定问题,不,您不能安全地删除这些操作码。
来自同一链接的另一个有趣的信息是,c#编译器规范不能保证此行为,并且到目前为止,它是一种实现细节。它可能不会改变,因为这是所有以前的编译器的行为,不仅是VS 2017。
关于c# - 显式基类型强制转换时可能多余的操作码,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41248157/