Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 以String.Format(…)格式装箱和取消装箱。。。以下是合理的吗?_C#_.net_Boxing - Fatal编程技术网

C# 以String.Format(…)格式装箱和取消装箱。。。以下是合理的吗?

C# 以String.Format(…)格式装箱和取消装箱。。。以下是合理的吗?,c#,.net,boxing,C#,.net,Boxing,我正在阅读一些有关装箱/取消装箱的内容,结果表明,如果执行普通的String.Format(),在object[]参数列表中有一个值类型,则会导致装箱操作。例如,如果您试图打印一个整数的值并执行string.Format(“我的值是{0}”,myVal),它会将您的myValint粘贴在一个框中,并对其运行ToString函数 浏览 在将值类型传递给string.Format函数:string.Format(“我的值是{0}”,myVal.ToString()) 这是真的吗?我倾向于相信作者的观

我正在阅读一些有关装箱/取消装箱的内容,结果表明,如果执行普通的
String.Format()
,在
object[]
参数列表中有一个值类型,则会导致装箱操作。例如,如果您试图打印一个整数的值并执行
string.Format(“我的值是{0}”,myVal)
,它会将您的
myVal
int
粘贴在一个框中,并对其运行
ToString
函数

浏览

在将值类型传递给string.Format函数:
string.Format(“我的值是{0}”,myVal.ToString())

  • 这是真的吗?我倾向于相信作者的观点 证据
  • 如果这是真的,为什么编译器不直接这样做呢 为你?也许是2006年以来的变化?有人知道吗?(我没有时间/经验做整个IL分析)

  • 编译器不会为您执行此操作,因为
    string.Format
    接受
    params对象[]
    。装箱是由于转换到
    对象
    而发生的

    我不认为编译器倾向于使用特殊的case方法,所以在这种情况下它不会删除装箱

    是的,在许多情况下,如果您首先调用
    ToString()
    ,编译器确实不会执行装箱。如果它使用来自
    Object
    的实现,我认为它仍然必须使用box


    最终,格式字符串本身的
    string.Format
    解析要比任何装箱操作慢得多,因此开销可以忽略不计。

    1:是的,只要值类型覆盖
    ToString()
    ,所有内置类型都会这样做

    2:因为规范中没有定义此类行为,正确处理
    params对象[]
    (wrt值类型)的方法是:装箱

    Format就像其他不透明的方法一样;它将这样做的事实对编译器来说是不透明的。如果模式包含像
    {0:n2}
    这样的格式(这需要特定的转换,而不仅仅是
    ToString()
    ),那么在功能上也是不正确的。试图理解该模式是不可取的,也是不可靠的,因为在运行时之前可能不知道该模式。

    string.Format(“我的值是{0}”,myVal)
    string.Format("My value is {0}", myVal)<br>
    myVal is an object<br><br>
    
    string.Format("My value is {0}",myVal.ToString())<br>
    myVal.ToString() is a string<br><br>
    
    myVal是一个对象

    Format(“我的值是{0}”,myVal.ToString())
    myVal.ToString()是一个字符串


    ToString过载,因此编译器无法为您做出决定。

    为什么不尝试每种方法1亿次左右,看看需要多长时间:

    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
    
        int myVal = 6;
    
        sw.Start();
    
        for (int i = 0; i < 100000000; i++)
        {
            string string1 = string.Format("My value is {0}", myVal);
        }
    
        sw.Stop();
    
        Console.WriteLine("Original method - {0} milliseconds", sw.ElapsedMilliseconds);
    
        sw.Reset();
    
        sw.Start();
    
        for (int i = 0; i < 100000000; i++)
        {
            string string2 = string.Format("My value is {0}", myVal.ToString());
        }
    
        sw.Stop();
    
        Console.WriteLine("ToStringed method - {0} milliseconds", sw.ElapsedMilliseconds);
    
        Console.ReadLine();
    }
    
    static void Main(字符串[]args)
    {
    秒表sw=新秒表();
    int-myVal=6;
    sw.Start();
    对于(int i=0;i<100000000;i++)
    {
    string1=string.Format(“我的值是{0}”,myVal);
    }
    sw.Stop();
    WriteLine(“原始方法-{0}毫秒”,sw.elapsedmillisons);
    sw.Reset();
    sw.Start();
    对于(int i=0;i<100000000;i++)
    {
    string string2=string.Format(“我的值是{0}”,myVal.ToString());
    }
    sw.Stop();
    WriteLine(“toString方法-{0}毫秒”,sw.elapsedmillisons);
    Console.ReadLine();
    }
    

    在我的机器上,我发现.toString版本运行的时间大约是原始版本运行时间的95%,因此有一些经验证据表明它有轻微的性能优势。

    最好通过使用或构造字符串并使用类型化重载来避免装箱


    大多数时候,拳击应该不太重要,甚至不值得你注意。

    首先是简单的一项。编译器没有将
    string.Format(“{0}”,myVal)
    转换为
    string.Format{0},myVal.ToString())
    的原因是没有理由这样做。它是否应该将
    BlahFooBlahBlah(myVal)
    转换为
    BlahFooBlahBlah(myVal.ToString())
    ?也许这会有同样的效果,但为了更好的性能,但很可能会引入错误。糟糕的编译器!没有饼干

    除非可以从一般原则中推理出一些东西,否则编译器应该不去管它

    现在来看一个有趣的话题:为什么前者会导致拳击,而后者不会

    对于前者,由于唯一匹配的签名是
    string.Format(string,object)
    ,因此必须将整数转换为一个对象(装箱)以传递给方法,该方法期望接收一个字符串和一个对象

    不过,另一半原因是,为什么
    myVal.ToString()
    box不也是如此

    当编译器处理这段代码时,它具备以下知识:

  • myVal是一个Int32
  • ToString()由Int32定义
  • Int32是一种值类型,因此:
  • myVal不可能是空引用*并且:
  • 不可能有更派生的重写-Int32.ToString()已有效密封
  • 现在,C#编译器对所有方法调用使用
    callvirt
    有两个原因。第一个原因是,有时你确实希望它毕竟是一个虚拟调用。第二个原因是(更有争议的是)他们决定禁止对空引用的任何方法调用,而
    callvirt
    有一个内置测试

    但是在这种情况下,这两种方法都不适用。不能有更派生的类重写Int32.ToString(),myVal不能为null。因此,它可以向
    ToString()
    方法引入
    调用
    ,该方法无需装箱即可传递
    Int32

    这种组合(值不能为null,方法不能在其他地方重写)只会很少出现引用类型,因此编译器无法充分利用它(因为它们不必是