developer tip

변경 불가능한 구조체에 대해 공용 읽기 전용 필드를 사용합니까?

copycodes 2020. 12. 11. 08:23
반응형

변경 불가능한 구조체에 대해 공용 읽기 전용 필드를 사용합니까?


이것이 변경 불가능한 구조체를 선언하는 적절한 방법입니까?

public struct Pair
{
    public readonly int x;
    public readonly int y;

    // Constructor and stuff
}

왜 이것이 문제가 될지 생각할 수 없지만 확인하고 싶었습니다.

이 예에서는 int를 사용했습니다. 대신 클래스를 사용했지만 그 클래스도 그렇게 불변하면 어떨까요? 그것도 잘 작동합니다.

public struct Pair
{
    public readonly (immutableClass) x;
    public readonly (immutableClass) y;

    // Constructor and stuff
}

(제외 : 속성을 사용하는 것이 더 일반화되고 변경할 수 있다는 것을 이해하지만이 구조체는 문자 그대로 두 개의 값을 저장하기위한 것입니다. 여기에서 불변성 질문에 관심이 있습니다.)


구조체를 사용하려면 불변으로 만드는 것이 가장 좋습니다.

모든 필드를 읽기 전용으로 만드는 것은 (1) 구조체가 불변임을 문서화하고 (2) 우발적 인 변형을 방지하는 데 도움이되는 좋은 방법입니다.

그러나 한 가지 주름이 있습니다. 사실 이상한 우연의 일치로 다음 주에 블로그를 만들 계획이었습니다. , 구조체 필드의 읽기 전용은 거짓말 입니다. 읽기 전용 필드는 변경 될 수 없다고 예상하지만 물론 변경할 수 있습니다. 구조체 필드의 "readonly"는 계정에 돈이없는 수표를 작성하는 선언입니다. 구조체는 저장소를 소유하지 않으며 변경할 수있는 저장소입니다.

예를 들어, 구조체를 보겠습니다.

public struct Pair
{
    public readonly int x;
    public readonly int y;
    public Pair(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    public void M(ref Pair p)
    {
        int oldX = x;
        int oldY = y;
        // Something happens here
        Debug.Assert(x == oldX);
        Debug.Assert(y == oldY);
    }
}

디버그 어설 션을 위반하는 "여기에서 어떤 일이 발생 함"에서 발생할 수있는 일이 있습니까? 확실한.

    public void M(ref Pair p)
    {
        int oldX = this.x;
        int oldY = this.y;
        p = new Pair(0, 0);
        Debug.Assert(this.x == oldX);
        Debug.Assert(this.y == oldY);
    }
...
    Pair myPair = new Pair(10, 20);
    myPair.M(ref myPair);

And now what happens? The assertion is violated! "this" and "p" refer to the same storage location. The storage location is mutated, and so the contents of "this" are mutated because they are the same thing. The struct is not able to enforce the read-only-ness of x and y because the struct doesn't own the storage; the storage is a local variable that is free to mutate as much as it wants.

You cannot rely on the invariant that a readonly field in a struct is never observed to change; the only thing you can rely on is that you can't write code that directly changes it. But with a little sneaky work like this you can indirectly change it all you want.

See also Joe Duffy's excellent blog article on this issue:

http://joeduffyblog.com/2010/07/01/when-is-a-readonly-field-not-readonly/


That would make it immutable indeed. I suppose you better add a constructor though.
If all its members are immutable too, this would make it entirely immutable. These can be classes or simple values.


As of C# 7.2, you can now declare an entire struct as immutable:

public readonly struct Pair
{
    public int x;
    public int y;

    // Constructor and stuff
}

This will have the same effect as marking all of the fields as readonly, and will also document to the compiler itself that the struct is immutable. This will increase the performance of areas where the struct is used by reducing the number of defensive copies the compiler makes.

As noted in Eric Lippert's answer, this does not prevent the structure itself from being reassigned completely, and thus providing the effect of its fields changing out from under you. Either passing by value or using the new in parameter modifier can be used to help prevent this:

public void DoSomething(in Pair p) {
    p.x = 0; // illegal
    p = new Pair(0, 0); // also illegal
}

The compiler will forbid assignment to readonly fields as well as read-only properties.

I recommend using read-only properties mostly for public interface reasons and data-binding (which won't work on fields). If it were my project I would require that if the struct/class is public. If it's going to be internal to an assembly or private to a class, I could overlook it at first and refactor them to read-only properties later.

참고URL : https://stackoverflow.com/questions/6063212/does-using-public-readonly-fields-for-immutable-structs-work

반응형