gpt4 book ai didi

c# - 这是传递给带有 in 关键字的方法的只读结构的防御副本吗

转载 作者:太空狗 更新时间:2023-10-29 23:49:00 25 4
gpt4 key购买 nike

我正在尝试将 readonly struct 传递给带有 in 修饰符的方法。当我查看生成的 IL 代码时,似乎生成了只读结构的防御性副本。

只读结构定义为

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 似乎复制了 point1point2 参数:

// [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 指令,我认为这意味着加载 point1point2 的地址。

我的理解是否正确,制作了防御副本?或者我对 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

另一个观察是,如果我从接受 ReadonlyPoint3DCalculateDisctance 方法中删除 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对于这段代码。差异 - 仅针对 CalculateDistance(in MutablePoint3D point1, in MutablePoint3D point2) 看到的 ldobj 指令将解释防御复制仅针对这种情况完成。

但是,问题中发布的 IL 指令取自 ReSharper 的 IL Viewer,并由 ILDASM.exe 工具(用于发布配置,如 sharplab.io)验证。所以我不确定这种差异从何而来以及哪些输出值得信任。

最佳答案

可以在 related GitHub issue 上找到长篇讨论.

本质上这是一个 Roslyn bug已修复,最新版本的 VS 2019(16.2 及更高版本)已修复。

关于c# - 这是传递给带有 in 关键字的方法的只读结构的防御副本吗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57126134/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com