![](/img/trans.png)
[英]What is the life expectency of structs when returned from method and passed as parameters?
[英]Are mutable structs copied when passed through methods via in parameters?
我想尽量减少数学库中结构的复制,并阅读修饰符中的 C# 7.2 in
尤其是在将它与可变结构一起使用时的警告。
碰巧我有这个可变结构:
public struct Quaternion
{
public float W;
public float X;
public float Y;
public float Z;
}
到目前为止,该库有这样的方法,其中参数由ref
传递:
public static void Dot(ref Quaternion left, ref Quaternion right, out float result)
=> result = left.W * right.W + left.X * right.X + left.Y * right.Y + left.Z * right.Z;
从 MSDN 文档中,我了解到,如果我将这些更改为in
参数,只要我只访问可变结构的字段,就不会发生防御性复制,因为编译器看到我没有修改可变结构:
public static void Dot(in Quaternion left, in Quaternion right, out float result)
=> result = left.W * right.W + left.X * right.X + left.Y * right.Y + left.Z * right.Z;
第一个问题:我对这种行为的理解是否正确?
第二,愚蠢的问题:如果在其中一个接受结构作为in
参数的方法中,如果我调用另一个接受它们作为in
参数的方法,编译器会复制它吗? 一个例子:
public static void Lerp(in Quaternion start, in Quaternion end, float amount,
out Quaternion result)
{
float inv = 1.0f - amount;
if (Dot(start, end) >= 0.0f) // will 2 copies be created here?
{
result.W = inv * start.W + amount * end.W;
result.X = inv * start.X + amount * end.X;
result.Y = inv * start.Y + amount * end.Y;
result.Z = inv * start.Z + amount * end.Z;
}
else
{
result.W = inv * start.W - amount * end.W;
result.X = inv * start.X - amount * end.X;
result.Y = inv * start.Y - amount * end.Y;
result.Z = inv * start.Z - amount * end.Z;
}
result.Normalize();
}
我很确定它不应该创建副本——那我还能如何防止来自调用方的副本呢? 但由于我不确定,我最好在制造混乱之前先问清楚。
附录
我想将ref
更改为in
原因:
static
) readonly
字段(例如特定常量四元数)不能作为ref
arguments 传递。ref
,但我可以in
.ref
是丑陋的。 如评论中所述,如果运行时不能保证传递的实例不被修改,则使用in
for 可变结构的参数可以创建防御性副本。 如果您在该实例上调用属性、索引器或方法,可能很难保证这一点。
因此,当您不打算修改实例时,您应该通过将它们设为readonly
来明确地 state 。 如果您尝试修改其中的实例,这也会导致编译失败。
请特别注意以下示例中readonly
关键字的位置:
public struct Vec2
{
public float X, Y;
// Properties
public readonly float Length
{
get { return MathF.Sqrt(LengthSq); }
}
public readonly float LengthSq => X * X + Y * Y;
// Indexers (syntax the same for properties if they also have setter)
public float this[int index]
{
readonly get => index switch
{
0 => X,
1 => Y,
_ => throw ...
};
set
{
switch (index)
{
case 0: X = value; break;
case 1: Y = value; break;
default: throw ...
}
}
}
// Methods
public readonly override int GetHashCode() => HashCode.Combine(X, Y);
}
现在,只要你有一个使用带有in
修饰符的Vec2
的方法,你就可以安全地调用上面的方法而不需要复制。
(这个功能是在 C# 8.0 中引入的,当我问这个问题时不可用。 )
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.