这是传递给带有 in 关键字的方法的只读结构的防御副本吗
Is this a defensive copy of readonly struct passed to a method with in keyword
我正在尝试将 readonly struct
传递给带有 in
修饰符的方法。
当我查看生成的 IL 代码时,似乎生成了只读结构的防御性副本。
readonly struct
定义为
public readonly struct ReadonlyPoint3D
{
public ReadonlyPoint3D(double x, double y, double z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public double X { get; }
public double Y { get; }
public double Z { get; }
}
接受的方法ReadonlyPoint3D
private static double CalculateDistance(in ReadonlyPoint3D point1, in ReadonlyPoint3D point2)
{
double xDifference = point1.X - point2.X;
double yDifference = point1.Y - point2.Y;
double zDifference = point1.Z - point2.Z;
return Math.Sqrt(xDifference * xDifference + yDifference * yDifference + zDifference * zDifference);
}
以及我调用此方法的方式:
static void Main(string[] args)
{
var point1 = new ReadonlyPoint3D(0, 0, 0);
var point2 = new ReadonlyPoint3D(1, 1, 1);
var distance = CalculateDistance(in point1, in point2);
}
如果我查看为 CalculateDistance
方法调用生成的 IL,我发现 ReadonlyPoint3D
个实例是 passed by reference:
IL_0045: ldloca.s point1
IL_0047: ldloca.s point2
IL_0049: call float64 CSharpTests.Program::CalculateDistance(valuetype CSharpTests.ReadonlyPoint3D&, valuetype CSharpTests.ReadonlyPoint3D&)
IL_004e: stloc.2 // distance
然而,CalculateDistance
方法的 IL 似乎复制了 point1
& point2
参数:
// [25 9 - 25 10]
IL_0000: nop
// [26 13 - 26 54]
IL_0001: ldarg.0 // point1
IL_0002: call instance float64 CSharpTests.ReadonlyPoint3D::get_X()
IL_0007: ldarg.1 // point2
IL_0008: call instance float64 CSharpTests.ReadonlyPoint3D::get_X()
IL_000d: sub
IL_000e: stloc.0 // xDifference
// the resit is omitted for the sake of brevity, essentially same code repeated for Y & Z
CalculateDistance
方法生成的 IL 中的 ldarg.0
& ldarg.1
让我认为 point1
& point2
的副本已经制作完成。
我期待在这里看到的是 ldloca.s
指令,我认为这意味着加载 point1
& point2
.
的地址
我的理解正确吗,制作防御副本?
或者我对 IL 代码的解释是错误的?
我正在使用 .NET Core 2.1 和 C# 7.3
编辑
根据 Microsoft 文档,使用 in
修饰符传递的可变结构将具有 defensive copies created.
如果我定义可变结构
public struct MutablePoint3D
{
public MutablePoint3D(double x, double y, double z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
并用 in
传递
private static double CalculateDistance(in MutablePoint3D point1, in MutablePoint3D point2)
{
double xDifference = point1.X - point2.X;
double yDifference = point1.Y - point2.Y;
double zDifference = point1.Z - point2.Z;
return Math.Sqrt(xDifference * xDifference + yDifference * yDifference + zDifference * zDifference);
}
我可以看到生成的 IL 代码与 readonly struct
生成的代码相似:
// [26 13 - 26 54]
IL_0001: ldarg.0 // point1
IL_0002: call instance float64 CSharpTests.MutablePoint3D::get_X()
IL_0007: ldarg.1 // point2
IL_0008: call instance float64 CSharpTests.MutablePoint3D::get_X()
IL_000d: sub
IL_000e: stloc.0 // xDifference
// the resit is omitted for the sake of brevity
另一个观察结果是,如果我从接受 ReadonlyPoint3D
的 CalculateDisctance
方法中删除 in
修饰符,生成的 IL 代码就是我所期望的
// [35 13 - 35 54]
IL_0001: ldarga.s point1
IL_0003: call instance float64 CSharpTests.ReadonlyPoint3D::get_X()
IL_0008: ldarga.s point2
IL_000a: call instance float64 CSharpTests.ReadonlyPoint3D::get_X()
IL_000f: sub
IL_0010: stloc.0 // xDifference
但这似乎并不对应the suggestion in Microsoft Docs
编辑 2
正如@PetSerAl 在评论中所建议的那样,sharplab.io produces different IL 此代码。
差异 - ldobj
仅针对 CalculateDistance(in MutablePoint3D point1, in MutablePoint3D point2)
的指令将解释防御性复制仅针对这种情况进行。
但是,问题中发布的 IL 指令取自 ReSharper 的 IL Viewer,并由 ILDASM.exe 工具验证(对于发布配置,如 sharplab.io 中)。所以我不确定这种差异从何而来以及哪个输出值得信任。
可以在 related GitHub issue 上找到长篇讨论。
本质上这是一个 Roslyn bug 已修复,最新版本的 VS 2019(16.2 及更高版本)已修复。
我正在尝试将 readonly struct
传递给带有 in
修饰符的方法。
当我查看生成的 IL 代码时,似乎生成了只读结构的防御性副本。
readonly struct
定义为
public readonly struct ReadonlyPoint3D
{
public ReadonlyPoint3D(double x, double y, double z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public double X { get; }
public double Y { get; }
public double Z { get; }
}
接受的方法ReadonlyPoint3D
private static double CalculateDistance(in ReadonlyPoint3D point1, in ReadonlyPoint3D point2)
{
double xDifference = point1.X - point2.X;
double yDifference = point1.Y - point2.Y;
double zDifference = point1.Z - point2.Z;
return Math.Sqrt(xDifference * xDifference + yDifference * yDifference + zDifference * zDifference);
}
以及我调用此方法的方式:
static void Main(string[] args)
{
var point1 = new ReadonlyPoint3D(0, 0, 0);
var point2 = new ReadonlyPoint3D(1, 1, 1);
var distance = CalculateDistance(in point1, in point2);
}
如果我查看为 CalculateDistance
方法调用生成的 IL,我发现 ReadonlyPoint3D
个实例是 passed by reference:
IL_0045: ldloca.s point1
IL_0047: ldloca.s point2
IL_0049: call float64 CSharpTests.Program::CalculateDistance(valuetype CSharpTests.ReadonlyPoint3D&, valuetype CSharpTests.ReadonlyPoint3D&)
IL_004e: stloc.2 // distance
然而,CalculateDistance
方法的 IL 似乎复制了 point1
& point2
参数:
// [25 9 - 25 10]
IL_0000: nop
// [26 13 - 26 54]
IL_0001: ldarg.0 // point1
IL_0002: call instance float64 CSharpTests.ReadonlyPoint3D::get_X()
IL_0007: ldarg.1 // point2
IL_0008: call instance float64 CSharpTests.ReadonlyPoint3D::get_X()
IL_000d: sub
IL_000e: stloc.0 // xDifference
// the resit is omitted for the sake of brevity, essentially same code repeated for Y & Z
CalculateDistance
方法生成的 IL 中的 ldarg.0
& ldarg.1
让我认为 point1
& point2
的副本已经制作完成。
我期待在这里看到的是 ldloca.s
指令,我认为这意味着加载 point1
& point2
.
我的理解正确吗,制作防御副本? 或者我对 IL 代码的解释是错误的?
我正在使用 .NET Core 2.1 和 C# 7.3
编辑
根据 Microsoft 文档,使用 in
修饰符传递的可变结构将具有 defensive copies created.
如果我定义可变结构
public struct MutablePoint3D
{
public MutablePoint3D(double x, double y, double z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
并用 in
private static double CalculateDistance(in MutablePoint3D point1, in MutablePoint3D point2)
{
double xDifference = point1.X - point2.X;
double yDifference = point1.Y - point2.Y;
double zDifference = point1.Z - point2.Z;
return Math.Sqrt(xDifference * xDifference + yDifference * yDifference + zDifference * zDifference);
}
我可以看到生成的 IL 代码与 readonly struct
生成的代码相似:
// [26 13 - 26 54]
IL_0001: ldarg.0 // point1
IL_0002: call instance float64 CSharpTests.MutablePoint3D::get_X()
IL_0007: ldarg.1 // point2
IL_0008: call instance float64 CSharpTests.MutablePoint3D::get_X()
IL_000d: sub
IL_000e: stloc.0 // xDifference
// the resit is omitted for the sake of brevity
另一个观察结果是,如果我从接受 ReadonlyPoint3D
的 CalculateDisctance
方法中删除 in
修饰符,生成的 IL 代码就是我所期望的
// [35 13 - 35 54]
IL_0001: ldarga.s point1
IL_0003: call instance float64 CSharpTests.ReadonlyPoint3D::get_X()
IL_0008: ldarga.s point2
IL_000a: call instance float64 CSharpTests.ReadonlyPoint3D::get_X()
IL_000f: sub
IL_0010: stloc.0 // xDifference
但这似乎并不对应the suggestion in Microsoft Docs
编辑 2
正如@PetSerAl 在评论中所建议的那样,sharplab.io produces different IL 此代码。
差异 - ldobj
仅针对 CalculateDistance(in MutablePoint3D point1, in MutablePoint3D point2)
的指令将解释防御性复制仅针对这种情况进行。
但是,问题中发布的 IL 指令取自 ReSharper 的 IL Viewer,并由 ILDASM.exe 工具验证(对于发布配置,如 sharplab.io 中)。所以我不确定这种差异从何而来以及哪个输出值得信任。
可以在 related GitHub issue 上找到长篇讨论。
本质上这是一个 Roslyn bug 已修复,最新版本的 VS 2019(16.2 及更高版本)已修复。