읽기 전용 속성을 구현하는 방법


80

내 유형에 읽기 전용 속성 을 구현해야 합니다. 또한이 속성의 값은 생성자에서 설정되며 변경되지 않습니다 (WPF에 대한 사용자 지정 라우팅 UI 명령을 노출하는 클래스를 작성하고 있지만 중요하지 않습니다).

두 가지 방법이 있습니다.

  1. class MyClass
    {
        public readonly object MyProperty = new object();
    }
    
  2. class MyClass
    {
        private readonly object my_property = new object();
        public object MyProperty { get { return my_property; } }
    }
    

이 모든 FxCop 오류가 퍼블릭 멤버 변수를 가져서는 안된다는 오류와 함께 두 번째가 올바른 방법 인 것 같습니다. 옳은?

이 경우 가져 오기 전용 속성과 읽기 전용 멤버간에 차이점이 있습니까?

나는 어떤 의견 / 조언 / 등을 감사 할 것입니다.


31
때때로 자동 속성 구문에 get; readonly set;옵션이 포함되기를 바랍니다 .
Dan Bryant


@DanBryant get; private set;적어도 있습니다.
Marc.2377

2
@ Marc.2377, 사실, 그들은 {get;}얼마 전에 지원을 추가 하여이 문제를 해결했습니다.
Dan Bryant

@DanBryant 아, 참으로. 나는 먼저 답을 읽어야했다;)
Marc.2377

답변:


51

버전 관리 :
소스 호환성에만 관심이 있다면 큰 차이가 없다고 생각합니다.
속성을 사용하면 라이브러리에 따라 컴파일 된 코드를 손상시키지 않고 setter가있는 속성으로 대체 할 수 있으므로 바이너리 호환성에 더 좋습니다.

대회 : 대회
를 따르고 있습니다. 이와 같은 경우 두 가능성의 차이가 관례에 따라 비교적 사소한 경우가 더 좋습니다. 다시 물릴 수있는 한 가지 경우는 반사 기반 코드입니다. 속성 편집기 / 뷰어와 같이 필드가 아닌 속성 만 허용 할 수 있습니다.

Serialization
필드에서 속성으로 변경하면 많은 serializer가 손상 될 수 있습니다. 그리고 AFAIK XmlSerializer는 공용 필드가 아닌 공용 속성 만 직렬화합니다.

Autoproperty 사용
또 다른 일반적인 변형은 private setter와 함께 autoproperty를 사용하는 것 입니다. 짧고 속성이지만 읽기 전용을 적용하지 않습니다. 그래서 나는 다른 것을 선호합니다.

읽기 전용 필드는 자체 문서화
입니다. 그러나 필드의 한 가지 장점이 있습니다
. 공개 인터페이스에서 실제로 변경 불가능하다는 것을 한 눈에 분명히 보여줍니다 (반사를 제외하고). 부동산의 경우에만 볼 수 있습니다. 당신이 문서 또는 구현을 참조해야 할 것이다, 그래서 그것을 변경할 수 없습니다.

그러나 솔직히 나는 게으 르기 때문에 응용 프로그램 코드에서 첫 번째 것을 자주 사용합니다. 도서관에서는 일반적으로 더 철저하고 규칙을 따릅니다.

C # 6.0은 읽기 전용 자동 속성을 추가합니다.

public object MyProperty { get; }

따라서 이전 컴파일러를 지원할 필요가 없을 때 읽기 전용 필드만큼 간결한 코드로 진정한 읽기 전용 속성을 가질 수 있습니다.


1
현재 C # 9 추가를 public type prop => get;
원합니다

66

두 번째 방법은 선호하는 옵션입니다.

private readonly int MyVal = 5;

public int MyProp { get { return MyVal;}  }

이렇게하면 MyVal초기화시에만 할당 할 수 있습니다 (생성자에서도 설정할 수 있음).

언급했듯이-이렇게하면 내부 구성원을 노출하지 않으므로 나중에 내부 구현을 변경할 수 있습니다.


감사. 첫 번째 옵션도 동일합니다. 이 옵션이 선호되는 이유가 무엇이라고 생각하십니까?
akonsu 2010 년

우리가 사용하지 말아야 할 이유가 public int MyProp { get; private set; }있습니까? 나는 그것이 진정으로 읽기 전용이 아니라는 것을 알고 있지만 꽤 가까이에 있습니다.
Mike Hofer

3
@Mike Hofer-int는 readonly 로 선언 되었기 때문에 생성자 외부에서 변경할 수 없습니다.
Oded

5
@Mike Hofer-그것은 의도가 무엇인지에 달려 있습니다 ... 내가 작성한 버전은 초기화 후에 값을 변경할 수없는 내부 멤버를 노출합니다. Yours는 초기화 후 값이 변경 될 수있는 멤버를 노출합니다. 정말로 당신이 원하는 것에 달려 있습니다 ... 내 것은 읽기 전용입니다. init 후에는 전혀 변경할 수 없습니다. 당신의 것은 외부 클래스 에서처럼 읽기 전용입니다.
Oded

1
@Oded-나는 그것을 받아 들일 수 있습니다. 미묘하지만 중요한 차이입니다. 내부 및 외부 모두에서 상수로 작동하는 속성을 노출하려는 경우 유용 할 위치를 알 수 있습니다. 나는 확실히 그렇게하지 않을 것이다.
Mike Hofer

49

C # 6 (VS 2015)의 도입으로 이제 get암시 적 지원 필드가있는 자동 속성 만 가질 readonly수 있습니다 (즉, 값은 생성자에서 할당 할 수 있지만 다른 곳에서는 할당 할 수 없음).

public string Name { get; }

public Customer(string name)  // Constructor
{
    Name = name;
}

private void SomeFunction()
{
    Name = "Something Else";  // Compile-time error
}

이제 속성 (setter 포함 또는 제외)을 인라인으로 초기화 할 수도 있습니다.

public string Name { get; } = "Boris";

질문을 다시 살펴보면 옵션 1의 간결함과 함께 옵션 2 (공개 구성원은 필드가 아니라 속성 임)의 장점을 제공합니다.

불행히도 클래스의 소비자에게 setter가없는 것은 private setter가있는 것과 구별 할 수 없기 때문에 공용 인터페이스 수준 (@CodesInChaos의자가 문서화에 대한 요점에서와 같이) 수준에서 불변성을 보장하지 않습니다.


그러나 새 구문에 대한 좋은 정보는 개인 설정자를 반영하고 활성화 할 수 있고 / 또는 백업 필드에 값을로드 할 수 있습니다 (액세스 수정 자에 관계없이). 또한 개인 설정자와 실행시 설정자 부족을 구별 할 수 있습니다. -반사를 사용하는 시간.
Shaun Wilson

1
@Shaun : 좋은 지적- 반사로 할 수있는 일이 많이 있습니다! 많은 가능성이 원래 프로그래머 나 언어 디자이너의 의도에 어긋나지 만 readonly리플렉션을 사용하여 필드를 변경할 수 있다는 말입니까 (답은 모르겠지만 문제가있는 것 같습니다)?
Bob Sammers 2015 년

2
나는 특별히 그렇게 말한 것이 아니라, 대답은 : 예 .. 할 수 있습니다. gist.github.com/wilson0x4d/a053c0fd57892d357b2c 문제가 있다고 생각되면 지구상의 모든 운영 체제에 한 프로세스가 다른 프로세스의 메모리를 읽고 쓸 수있는 메커니즘이 있다는 것을 알 때까지 기다리십시오 (충분한 실행 권한이 주어지면, 즉.) 이것이 바로 소프트웨어 기반 시스템이 진정으로 안전 할 수없는 이유입니다. 그러나 저는이 질문이 흥미 롭더라도 원래의 질문과별로 관련이 없습니다.
Shaun Wilson

나는뿐만 아니라 상세한 기사 읽기 권하고 싶습니다 BillWagner의 기사
hastrb

13

다음과 같이 할 수 있습니다.

public int Property { get { ... } private set { ... } }

8
예, 가능합니다. 그러나이 기술을 사용하면 클래스 소비자가 속성을 수정할 수 없음을 보장 할뿐 객체의 수명 동안 일정하게 유지되는 것이 아닙니다.
Bob Sammers

Bob : 클래스의 소비자가 속성을 수정할 수없는 경우 속성은 "readOnly"입니다. 그렇지 않습니까?
El Bayames

@ElBayames 참조는 변경할 수 없지만 내부는 변경할 수 있습니다. 내부 상태로 개체를 노출 할 때 고려하십시오. 노출 된 get 메서드를 사용하여 개체를 쉽게 검색 한 다음 해당 개체의 내부 속성을 변경할 수 있습니다. 이것은 진정한 읽기 전용이 아닙니다.
Matthew S '

5

두 번째 방법이 더 바람직하다는 데 동의합니다. 이 기본 설정에 대한 유일한 실제 이유는 .NET 클래스에 공용 필드가 없다는 일반적인 기본 설정 때문입니다. 그러나 해당 필드가 읽기 전용이면 다른 속성과의 일관성 부족 외에 실제 이의가있는 것을 알 수 없습니다. 읽기 전용 필드와 가져 오기 전용 속성의 실제 차이점은 읽기 전용 필드는 해당 값이 개체의 수명 동안 변경되지 않고 가져 오기 전용 속성이 변경되지 않음을 보장한다는 것입니다.


4

두 번째 방법은 캡슐화 때문에 선호됩니다. 읽기 전용 필드를 공개 할 수는 있지만 데이터 액세스 권한이있는 C # 관용구는 필드가 아니라 속성을 통해 발생합니다.

그 이유는 속성이 공용 인터페이스를 정의하고 해당 속성에 대한 지원 구현이 변경 되어도 구현이 인터페이스 뒤에 숨겨져 있기 때문에 코드의 나머지 부분을 깨뜨리지 않기 때문입니다.



1

C # 9에서 Microsoft는 init;다음과 같은 방법을 사용하여 초기화시에만 속성을 설정하는 새로운 방법을 도입합니다 .

public class Person
{
  public string firstName { get; init; }
  public string lastName { get; init; }
}

이렇게하면 새 개체를 초기화 할 때 값을 할당 할 수 있습니다.

var person = new Person
{
  firstname = "John",
  lastName = "Doe"
}

그러나 나중에 변경할 수 없습니다.

person.lastName = "Denver"; // throws a compiler error
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.