以下数字赋值会导致C#隐式转换吗?

时间:2018-10-16 02:47:36

标签: c# performance casting

这个问题主要是出于好奇,因为我在官方C#文档中找不到任何东西,而且性能成本可能完全可以忽略。

基本上,我想知道两者之间是否存在运行时成本差异?

float i = 0;

float i = 0.0f;

我认为编译器足够聪明,可以在编译时将0转换为0.0f并直接进行分配而不进行强制转换。这个假设正确吗?

3 个答案:

答案 0 :(得分:3)

在您的代码示例中,两者没有什么不同。您已将类型声明为float,因此无论末尾是否带有f,它都将是浮点数。 f只是被忽略。

在执行方程式或分配通用变量(例如,使用var关键字)时,f起作用。

例如:

var value = 0; // this is assigned as integer.

var value = 0.0f // this is assigned as float.

var value = 0.0; // this is assigned as double.

这是一个计算示例:

注意: 要完全理解浮点是完全不同的一课,但要知道计算是根据整数,浮点(f),双精度(d)等类型进行不同的计算的或十进制(m)最重要

请注意,数学因类型而异。

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(1 / 3);
        Console.WriteLine(1f / 3);
        Console.WriteLine(1d / 3);
        Console.WriteLine(1m / 3);

        Console.ReadKey();
    }
    // OUTPUT
    // 0
    // 0.3333333
    // 0.333333333333333
    // 0.3333333333333333333333333333
}

还请注意,当您在计算中设置类型时,仅计算的那部分会得到该类型的结果。考虑以下示例中的混合类型:

class Program
{
    static void Main(string[] args)
    {
        //float + integer
        Console.WriteLine(1f / 3 + 1 / 3);

        //double + integer
        Console.WriteLine(1d / 3 + 1 / 3);

        //double + float
        Console.WriteLine(1d / 3 + 1 / 3f);

        //decimal + integer
        Console.WriteLine(1m / 3 + 1 / 3);

        //decimal + decimal
        Console.WriteLine(1m / 3 + 1 / 3m);

        Console.ReadKey();
    }
    // OUTPUT
    // 0.3333333
    // 0.333333333333333
    // 0.666666676600774
    // 0.3333333333333333333333333333
    // 0.6666666666666666666666666666
}

已更新,其中包含基于以下评论的信息

根据下面的评论以及对象的编译与运行时生成来更新我的答案。

所有已知类型都是在编译时生成的。因此,在您的问题中,是的,因为已知,编译器会将0赋给float。

所有泛型类型都具有在编译时生成的元数据;因此,编译器可以针对类型和标志错误回答问题。但是,类型本身直到运行时才生成。如果该类型是值类型或引用类型,则会更改其生成方式。考虑以下签名:

public void MyMethod<T>(T myType)

由于此方法是通用的,因此在运行时仅针对使用每种类型的值创建一个新方法,而对于每种值类型,则仅创建一次(每种值类型重复使用相同的生成类型)。看看下面的代码。

MyMethod(12);           // new type method built at runtime taking integer
MyMethod(12d);          // new type method built at runtime taking double
MyMethod(12);           // ** resuses already built type method taking integer
MyMethod(new Person()); // new type method built at runtime taking Person
MyMethod(new object()); // ** resuses type method built taking Person but uses object pointer.

因此您可以看到编译器可以帮助我们解决类型冲突,但是使用泛型类型的类型直到运行时才存在。而且重要的是要知道它们何时存在以及如何在值和引用类型之间使用它们。

最后我们有dynamic。这在编译时从未得到解决,实际上直到运行时才被忽略。您可以在这里做任何您想做的事;有点像编写JavaScript;那个叛逆的小恶魔。考虑以下代码:

static void Main(string[] args)
{
    dynamic value = 1;
    Console.WriteLine(value);

    int integer = value;
    Console.WriteLine(integer);

    value = "Hello World";
    Console.WriteLine(value);

    string text = value;
    Console.WriteLine(text);

    Console.ReadKey();
}
// OUTPUT
// 1
// 1
// Hello World
// Hello World

请注意,在这里,我不仅要采用未知类型,而且还要将其分配给已知类型...然后我将该类型完全更改为另一种类型,然后将其分配给另一种类型。编译器不在乎,它都可以工作...问题是如果我对其进行修改,以使事情不匹配,那么我将获得运行时异常,而不是编译时。根据您使用动态方式的不同,这可能是个大问题。

希望所有这些都有助于澄清一些事情。记得;已知类型(编译时),泛型(编译时元数据,运行时类型)和动态(运行时)。

答案 1 :(得分:2)

没有区别,请参见通过csharp labs IL代码反编译:

C#代码:

using System;
// Run mode:
//   value.Inspect()      — often better than Console.WriteLine
//   Inspect.Heap(object) — structure of an object in memory (heap)
//   Inspect.Stack(value) — structure of a stack value
public static class Program {
    public static void Main() {
        float i = 0;
        float i2 = 0.0f;

    }
}

IL代码:

.class private auto ansi '<Module>'
{
} // end of class <Module>

.class public auto ansi abstract sealed beforefieldinit Program
    extends [mscorlib]System.Object
{
    // Methods
    .method public hidebysig static 
        void Main () cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 14 (0xe)
        .maxstack 1
        .locals init (
            [0] float32,
            [1] float32
        )

        IL_0000: nop
        IL_0001: ldc.r4 0.0
        IL_0006: stloc.0
        IL_0007: ldc.r4 0.0
        IL_000c: stloc.1
        IL_000d: ret
    } // end of method Program::Main

} // end of class Program

答案 2 :(得分:1)

  float i = 0; // 'conversion' can be done at compile-time

形式上,从intfloat有一个隐式转换。但是,如果所讨论的int是编译时常量(例如本例中的文字0),则转换由C#编译器完成。

因此,您的两行代码都使用相同的程序(相同的IL)。

但是,在这种情况下:

static void M1(int n)
{
  float i = n; // actual conversion at run-time
  Console.WriteLine(i);
}

static void M2(float f)
{
  float i = f; // same types
  Console.WriteLine(i);
}

可能存在差异,因为M1需要从intInt32)转换为floatSingle),而{{1} }无需转换。