提问人:Ray 提问时间:11/4/2019 最后编辑:Ray 更新时间:6/20/2022 访问量:433
当通过 in 参数传递方法时,可变结构体是否被复制?
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;
第一个问题:我对这种行为的理解正确吗?
第二个,愚蠢的问题:如果在其中一个接受 struct 作为 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
- 我无法指定运算符参数,但我可以使用 .
ref
in
- 在呼叫站点上不断指定是丑陋的。
ref
- 我知道我必须在任何地方更改呼叫站点,但这没关系,因为这个库只会在内部使用。
答:
0赞
Ray
6/18/2022
#1
如注释中所述,如果运行时不能保证传递的实例不被修改,则使用 for 可变结构的参数可以创建防御性副本。如果在该实例上调用属性、索引器或方法,则可能很难保证这一点。in
因此,每当您不打算修改这样的实例时,您应该通过使它们明确说明这一点。这样做的好处是,如果您尝试修改其中的实例,也会导致编译失败。readonly
特别注意关键字在以下示例中的位置: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);
}
现在,每当您有一个与修饰符一起使用的方法时,您都可以安全地调用上述内容,而无需进行复制。Vec2
in
(此功能是在 C# 8.0 中引入的,当我提出问题时不可用。)
评论
in
readonly struct
readonly
ref