불변 필드에서 setter를 어떻게 처리합니까?


25

두 개의 readonly int필드 가있는 수업이 있습니다. 그들은 속성으로 노출됩니다 :

public class Thing
{
    private readonly int _foo, _bar;

    /// <summary> I AM IMMUTABLE. </summary>
    public Thing(int foo, int bar)
    {
        _foo = foo;
        _bar = bar;
    }

    public int Foo { get { return _foo; } set { } }

    public int Bar { get { return _bar; } set { } }
}

그러나 이는 다음이 완벽하게 유효한 코드임을 의미합니다.

Thing iThoughtThisWasMutable = new Thing(1, 42);

iThoughtThisWasMutable.Foo = 99;  // <-- Poor, mistaken developer.
                                  //     He surely has bugs now. :-(

작동 한다고 가정했을 때 발생 하는 버그 는 확실하지 않습니다. 물론 잘못된 개발자가 문서를 읽었을 것입니다. 그러나 컴파일 또는 런타임 오류가 문제에 대해 경고하지 않았다는 사실은 바뀌지 않습니다.

Thing개발자가 위의 실수를 저 지르지 않도록 클래스를 어떻게 변경 해야 합니까?

예외를 던져? 속성 대신 getter 메서드를 사용 하시겠습니까?



1
감사합니다, @gnat. 그 게시물 (질문과 대답 모두)은 Capital I 에서처럼 인터페이스에 대해 이야기하는 것 같습니다 Interfaces. 그것이 내가하고있는 일인지 잘 모르겠습니다.
kdbanman

6
@ gnat, 그것은 무서운 조언입니다. 인터페이스는 여전히 테스트하기 쉬운 공개적으로 변경 불가능한 VO / DTO를 제공하는 훌륭한 방법을 제공합니다.
David Arno

4
@gnat, 나는 그랬고 OP는 인터페이스가 불변성을 파괴한다고 생각하는 것처럼 보이지만 말도 안됩니다.
David Arno

1
@gnat, 그 질문은 DTO를위한 인터페이스 선언에 관한 것이었다. 가장 많이 투표 된 답변은 인터페이스가 유해하지는 않지만 불필요하다는 것입니다.
Paul Draper

답변:


107

왜 그 코드를 합법적으로 만드나요? 아무것도하지 않으면
꺼내십시오 set { }.
다음은 읽기 전용 공용 속성을 정의하는 방법입니다.

public int Foo { get { return _foo; } }

3
나는 그것이 어떤 이유로 합법적이라고 생각하지 않았지만 완벽하게 작동하는 것 같습니다! 고마워요
kdbanman

1
@kdbanman 아마도 IDE가 한 시점에서 수행하는 것을 보았을 수도 있고 아마도 자동 완성 일 수도 있습니다.
Panzercrisis

2
@kdbanman; 합법적이지 않은 것 (최대 C # 5)은 public int Foo { get; }(게터 만있는 자동 속성) public int Foo { get; private set; }입니다.
Laurent LA RIZZA

@LaurentLARIZZA, 당신은 내 기억을 조깅했습니다. 그것이 바로 나의 혼란이 시작된 곳입니다.
kdbanman

45

C # 5와 그 이전에는 게터를 통해 노출되는 불변 필드에 대한 두 가지 옵션에 직면했습니다.

  1. 읽기 전용 백업 변수를 작성하고 수동 게터를 통해이를 리턴하십시오. 이 옵션은 안전합니다 ( readonly불변성을 없애기 위해 명시 적으로를 제거해야합니다) . 그러나 많은 보일러 플레이트 코드를 만들었습니다.
  2. 개인 세터와 함께 자동 속성을 사용하십시오. 이렇게하면 코드가 단순 해지지 만 실수로 불변성을 깨뜨리기가 더 쉽습니다.

C # 6 (어제 출시 된 VS2015에서 사용 가능)을 사용하면 이제 읽기 전용 자동 속성이라는 두 가지 이점을 모두 얻을 수 있습니다. 이를 통해 OP 클래스를 단순화 할 수 있습니다.

public class Thing
{
    /// <summary> I AM IMMUTABLE. </summary>
    public Thing(int foo, int bar)
    {
        Foo = foo;
        Bar = bar;
    }

    public int Foo { get; }
    public int Bar { get; }
}

Foo = fooBar = bar라인보다는 (단순한 코드를 얻을 수있는)을 명시 적으로 정의 할 필요없이, 암시 (강력한 읽기 전용 요건을 달성한다) 생성자 및 배면 필드에 대해서만 유효하다.


2
그리고 기본 생성자는 생성자의 구문 오버 헤드를 제거하여이를 더욱 단순화 할 수 있습니다.
Sebastian Redl


1
@ DanLyons 젠장, 코드베이스 전체에서 이것을 사용하기를 고대했습니다.
Sebastian Redl

12

세터를 제거 할 수 있습니다. 그들은 아무 것도하지 않고 사용자를 혼란스럽게하며 버그로 이어질 것입니다. 그러나 대신 비공개로 만들 수 있으므로 지원 변수를 제거하여 코드를 단순화 할 수 있습니다.

public class Thing
{
    public Thing(int foo, int bar)
    {
        Foo = foo;
        Bar = bar;
    }

    public int Foo { get; private set; }

    public int Bar { get; private set; }
}

1
" public int Foo { get; private set; }"그 클래스는 스스로 변할 수 있기 때문에 더 이상 변경할 수 없습니다. 그래도 고마워!
kdbanman

4
설명 된 클래스는 변경되지 않으므로 변경할 수 없습니다.
David Arno

1
내 실제 수업은 내가 준 MWE 보다 조금 더 복잡합니다 . 나는 불변성 후에 스레드 안전성과 클래스 내부 보증으로 완성되었습니다.
kdbanman

4
@kdbanman, 그리고 요점은? 만약 당신의 클래스가 건설 중 개인 세터에게만 쓰면 "실제로 불변성"을 구현 한 것입니다. readonly키워드는 단지 포카 요케입니다 그것은 미래의 불변성을 깨는 클래스에 경비원, 즉; 불변성을 달성 할 필요는 없습니다.
David Arno

3
재미있는 용어, 나는 그것을 구글했다-poka-yoke는 "실수 방지"를 의미하는 일본어 용어입니다. 네가 옳아! 바로 내가하고있는 일입니다.
kdbanman

6

두 가지 솔루션.

단순한:

변경 불가능한 읽기 전용 개체에 대해 David가 지적한대로 setter를 포함하지 마십시오.

또는

세터가 새로운 변경 불가능한 오브젝트를 리턴 할 수 있도록 허용합니다 (이전 오브젝트에 비해 자세하지만 시간이 지남에 따라 초기화 된 각 오브젝트에 대한 상태 제공). 이 디자인은 모든 필수 OOP 언어로 확장되는 스레드 안전성 및 불변성에 매우 유용한 도구입니다.

의사 코드

public class Thing
{

{readonly vars}

    public Thing(int foo, int bar)
    {
        _foo = foo;
        _bar = bar;
    }

    public Thing withFoo(int foo) {
        return new Thing(foo, getBar());
    }

    public Thing withBar(int bar) {
        return new Thing(getFoo(), bar)
    }

    etc...
}

공공 정적 공장

public class Thing
{

{readonly vars}

    private Thing(int foo, int bar)
    {
        _foo = foo;
        _bar = bar;
    }

    public static with(int foo, int bar) {
        return new Thing(foo, bar)
    }

    public Thing withFoo(int foo) {
        return new Thing(foo, getBar());
    }

    public Thing withBar(int bar) {
        return new Thing(getFoo(), bar)
    }

    etc...
}

2
setXYZ는 팩토리 메소드의 끔찍한 이름이며 withXYZ 등을 고려하십시오.
벤 Voigt

감사합니다. 유용한 의견을 반영하여 답변을 업데이트했습니다. :-)
Matt Smithies

질문은 setter 메소드가 아닌 속성 setter 에 대해 구체적으로 묻고있었습니다 .
user253751

그러나 OP는 향후 개발자의 변경 가능한 상태에 대해 걱정하고 있습니다 . 비공개 읽기 전용 또는 비공개 최종 키워드를 사용하는 변수는 자체 문서화되어야하며, 이것이 이해되지 않으면 원래 개발자의 실수가 아닙니다. 상태를 변경하는 다른 방법을 이해하는 것이 중요합니다. 나는 매우 유용한 다른 접근법을 추가했습니다. 코드 세계에는 과도한 편집증을 유발하는 많은 것들이 있습니다. 개인 읽기 전용 var는 그중 하나가되어서는 안됩니다.
Matt Smithies
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.