C# C“实际上并没有复制整个”;“大结构”;从IL看传递论点?
我经常听说,对于值类型,我们在将它们传递到方法中时必须小心,因为值将被复制。如果结构很大,那么可能是个问题 不过,我看了一下这段代码的IL:C# C“实际上并没有复制整个”;“大结构”;从IL看传递论点?,c#,compilation,C#,Compilation,我经常听说,对于值类型,我们在将它们传递到方法中时必须小心,因为值将被复制。如果结构很大,那么可能是个问题 不过,我看了一下这段代码的IL: using System; public class Program { public struct Big { public int a, b, c, d; } public static void Main() { Big big = new Big(); Us
using System;
public class Program
{
public struct Big
{
public int a, b, c, d;
}
public static void Main()
{
Big big = new Big();
UseBig(big);
big.a += 7;
}
private static void UseBig(Big big)
{
big.a += 1;
big.b += 1;
}
}
IL:
调用函数的机制是,它将局部变量#0
的位置加载到堆栈中,然后在函数内部使用加载参数的地址
。然后我们可以使用这个地址加载字段
和存储字段
dup
因为load
和store
将使用堆栈顶部的地址
使用ref
再次运行:
using System;
public class Program
{
public struct Big
{
public int a, b, c, d;
}
public static void Main()
{
Big big = new Big();
UseBig(ref big);
big.a += 7;
}
private static void UseBig(ref Big big)
{
big.a += 1;
big.b += 1;
}
}
IL:
在输入函数时,它使用load local variable address
来代替它的值,因此地址进入函数内部,而不是加载它的地址,因为它已经是一个地址,所以我们直接加载参数
值。我们在这里所做的一切都会影响调用者的值,因为它是对调用者的地址而不是参数变量空间进行的。因此这就是ref
的行为
问题是:
struct
(c
和d
)中的任何其他变量?不管它有多大,程序实际上是以单个地址的形式传递整个结构。那么人们一直在谈论的巨大成本在哪里呢ref
实际上并没有节省任何“结构复制成本”这些IL代码从第一版中获得,副本由
ldloc.0
没有传递给函数的地址
无论如何,您不能依赖IL检查来实现这一点,请尝试查看生成的本机代码。即使在第一种情况下,内联也可能发生并优化掉该副本。在第一个版本中,副本由
ldloc.0
没有传递给函数的地址
无论如何,您不能依赖IL检查来实现这一点,请尝试查看生成的本机代码。即使在第一种情况下,内联也可能发生并优化掉该副本。也许这取决于实际的结构大小,但您的大小并没有那么大。通常在编程中,风险是由于过度推送堆栈上的项或递归而耗尽堆栈。第一个堆栈具有“load local#0”而不是“load location(address)of local#0”@MickyD那么你是说,如果我有1000个
long
而不是4个int
,即使我只使用前2个变量,IL也会改变?好吧,我自己错过了副本。也许这取决于实际的结构大小,你的没有那么大。通常在编程中,风险是由于过度推送堆栈上的项或递归而耗尽堆栈。第一个堆栈具有“load local#0”而不是“load location(address)of local#0”@MickyD那么你是说,如果我有1000个long
而不是只有4个int
,即使我只使用前2个变量,IL也会发生变化?好吧,我自己也错过了副本。。
ldloc.0