简体   繁体   中英

Are mutable structs copied when passed through methods via in parameters?

I want to minimize copying of structs in a maths library and read about the C# 7.2 in modifier, especially the warnings when using it with mutable structs.

It so happens that I have this mutable struct:

public struct Quaternion
{
    public float W;
    public float X;
    public float Y;
    public float Z;
}

So far, the library has methods like this, where parameters are passed by 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;

From the MSDN documentation, I learned that if I change these to in parameters, as long as I only access fields of a mutable struct, no defensive copy will occur since the compiler sees I am not modifying the mutable struct:

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;

First question: Is my understanding of that behavior correct?

Second, silly question: If in one of such methods which accept the struct as an in parameter, will the compiler copy it if I call another method accepting them as in parameters? An example:

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();
}

I am pretty sure it should not create copies - how else would I prevent copies from the call side then? But as I am not sure, I better ask first before creating a mess.


Addendum

Reasons I want to change ref to in :

  • ( static ) readonly fields (eg specific constant quaternions) cannot be passed as ref arguments.
  • I cannot specify ref on operator parameters, but I can use in .
  • Continually specifying ref on the call site is ugly.
  • I'm aware I have to change the call site everywhere, but that is okay since this library will only be used internally.

As mentioned in the comments, using in for parameters of mutable structs can create defensive copies if the runtime cannot guarantee that the passed instance is not modified. It may be hard to guarantee this if you call properties, indexers, or methods on that instance.

Ergo, whenever you do not intend to modify the instance in such, you should state this explicitly by making them readonly . This has the benefit of also causing compilation to fail if you attempt to modify the instance in them.

Note the placement of the readonly keyword in the following examples especially:

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);
}

Now, whenever you have a method using Vec2 with the in modifier, you can safely call the above without a copy being made.

( This feature was introduced in C# 8.0 and not available when I asked the question. )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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