“ double val = 1;”之间有区别吗?和“ double val = 1D;”?

时间:2019-11-18 14:14:41

标签: c# .net

以下两段代码之间有区别吗?

class Test {

    public readonly double Val;

    public Test(bool src) {
        this.Val = src ? 1 : 0;
    }

}

class Test {

    public readonly double Val;

    public Test(bool src) {
        this.Val = src ? 1D : 0D;
    }

}

我发现我们的代码库使用第二种编写方式。

3 个答案:

答案 0 :(得分:16)

这里有两个问题,需要注意的是,他们有不同的答案。

  

double val = 1;double val = 1D;之间有区别吗?

不。 C#编译器可以识别在预期为double的上下文中何时使用整数文字,并在编译时进行类型更改,因此这两个片段将生成相同的代码。

  

以下两段代码之间有区别吗?

double Val; 
...    
this.Val = src ? 1 : 0;
---
this.Val = src ? 1D : 0D;

是的。整数常量自动更改为双精度的规则仅适用于常数,而src ? ...不是常数。编译器将生成前者,就像您编写的一样:

int t;
if (src)
  t = 1;
else
  t = 0;
this.Val = (double)t;

第二个是

double t;
if (src)
  t = 1D;
else
  t = 0D;
this.Val = t;

也就是说,在第一个中,我们选择一个整数,然后将其转换为双精度,在第二个中,我们选择一个双精度。

仅供参考:C#编译器或抖动允许被允许认识到可以将第一个程序优化为第二个程序,但是我不知道它是否确实可以这样做。 C#编译器确实有时会将提升算术的转换移到条件主体中。我大约八年前编写了该代码,但是我不记得所有的细节。

答案 1 :(得分:6)

生成的IL代码中存在 差异。

本课程:

class Test1
{
    public readonly double Val;

    public Test1(bool src)
    {
        this.Val = src ? 1 : 0;
    }
}

为构造函数生成以下IL代码:

.class private auto ansi beforefieldinit Demo.Test1
    extends [mscorlib]System.Object
{
    .field public initonly float64 Val

    .method public hidebysig specialname rtspecialname instance void .ctor (
            bool src
        ) cil managed 
    {
        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.Object::.ctor()
        IL_0006: ldarg.0
        IL_0007: ldarg.1
        IL_0008: brtrue.s IL_000d

        IL_000a: ldc.i4.0
        IL_000b: br.s IL_000e

        IL_000d: ldc.i4.1

        IL_000e: conv.r8
        IL_000f: stfld float64 Demo.Test1::Val
        IL_0014: ret
    }
}

这堂课

class Test2
{
    public readonly double Val;

    public Test2(bool src)
    {
        this.Val = src ? 1d : 0d;
    }
}

为构造函数生成以下IL代码:

.class private auto ansi beforefieldinit Demo.Test2
    extends [mscorlib]System.Object
{
    .field public initonly float64 Val

    .method public hidebysig specialname rtspecialname instance void .ctor (
            bool src
        ) cil managed 
    {
        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.Object::.ctor()
        IL_0006: ldarg.0
        IL_0007: ldarg.1
        IL_0008: brtrue.s IL_0015

        IL_000a: ldc.r8 0.0
        IL_0013: br.s IL_001e

        IL_0015: ldc.r8 1

        IL_001e: stfld float64 Demo.Test2::Val
        IL_0023: ret
    }
}

如您所见,在第一个版本中,它必须调用conv.r8才能将int转换为double。

但是:(1)最终结果是相同的,并且(2)JIT编译器很可能将这两个都转换为相同的机器代码。

所以答案是:是的,有一个区别。但是您不必担心。

个人而言,我会选择第二个版本,因为它可以更好地表达程序员的意图,并且可能会产生效率更高的代码(取决于JIT编译器的工作方式)。 / p>

答案 2 :(得分:4)

没什么区别,编译器足够聪明,可以隐式地进行或不进行转换。
但是,如果使用var,则需要编写var val = 42D;来确保变量是双精度型而不是整数。

double foo = 1;  // This is a double having the value 1
double bar = 1d; // This is a double having the value 1

var val = 42d;   // This is a double having the value 42
var val2 = 42;   // /!\ This is an int having the value 42 !! /!\