有趣的问题。让我们测试一下:
public void AddPlayerData(in PlayerData pd)
{
pd.SpawnPoint.X = 42;
}
给出编译器错误:
只读字段“PlayerData.SpawnPoint”的成员不能被修改(除了在构造函数或变量中
由此我假设编译器不会创建任何防御性副本,因为非只读结构无论如何都不能被修改。可能存在一些允许结构更改的边缘情况,我对语言规范不够精通,无法确定这一点。
然而,讨论编译器优化是很困难的,因为只要结果相同,编译器通常可以自由地做它想做的任何事情,所以编译器版本之间的行为很可能会发生变化。像往常一样,建议是做一个基准来比较你的选择。
所以让我们这样做:
public readonly struct PlayerData1
{
public readonly Vector3 A;
public readonly Vector3 B;
public readonly Vector3 C;
public readonly Vector3 D;
public readonly Vector3 E;
public readonly Vector3 F;
public readonly Vector3 G;
public readonly Vector3 H;
}
public readonly struct PlayerData2
{
public readonly ReadonlyVector3 A;
public readonly ReadonlyVector3 B;
public readonly ReadonlyVector3 C;
public readonly ReadonlyVector3 D;
public readonly ReadonlyVector3 E;
public readonly ReadonlyVector3 F;
public readonly ReadonlyVector3 G;
public readonly ReadonlyVector3 H;
}
public readonly struct ReadonlyVector3
{
public readonly float X;
public readonly float Y;
public readonly float Z;
}
public static float Sum1(in PlayerData1 pd) => pd.A.X + pd.D.Y + pd.H.Z;
public static float Sum2(in PlayerData2 pd) => pd.A.X + pd.D.Y + pd.H.Z;
[Test]
public void TestInParameterPerformance()
{
var pd1 = new PlayerData1();
var pd2 = new PlayerData2();
// Do warmup
Sum1(pd1);
Sum2(pd2);
float sum1 = 0;
var sw1 = Stopwatch.StartNew();
for (int i = 0; i < 1000000000; i++)
{
sum1 += Sum1(pd1);
}
float sum2 = 0;
sw1.Stop();
var sw2 = Stopwatch.StartNew();
for (int i = 0; i < 1000000000; i++)
{
sum2 += Sum2(pd2);
}
sw2.Stop();
Console.WriteLine("Sum1: " + sw1.ElapsedMilliseconds);
Console.WriteLine("Sum2: " + sw2.ElapsedMilliseconds);
}
对我来说,使用 .Net framework 4.8 这给出了
Sum1: 1035
Sum2: 1027
即在测量误差范围内。所以我不会担心。