繁体   English   中英

通过 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;

第一个问题:我对这种行为的理解是否正确?

第二,愚蠢的问题:如果在其中一个接受结构作为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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM