스레드 안전성은 항상 불변 유형 및 특히 컬렉션을 사용하는 주요 이점으로 항상 언급되는 것처럼 보입니다.
메서드가 문자열 사전 (C #에서는 변경할 수 없음)을 수정하지 않도록하려는 상황이 있습니다. 가능한 한 많이 제한하고 싶습니다.
그러나 새 패키지 (Microsoft Immutable Collections)에 종속성을 추가하는 것이 가치가 있는지 확실하지 않습니다. 성능도 큰 문제가 아닙니다.
따라서 어려운 성능 요구 사항이없고 스레드 안전 문제가없는 경우 불변 컬렉션이 강력하게 권장 되는지 여부가 제 질문입니다 . 값 의미론 (내 예제 에서처럼)도 요구 사항 일 수도 있고 아닐 수도 있습니다.
답변
불변성은 나중에 코드를 읽을 때 정신적으로 추적해야하는 정보의 양을 단순화합니다 . 변경 가능한 변수, 특히 변경 가능한 클래스 멤버의 경우 디버거를 사용하여 코드를 실행하지 않고 읽고있는 특정 라인에서 어떤 상태에 있는지 알기가 매우 어렵습니다. 변경 불가능한 데이터는 추론하기 쉽습니다 . 항상 동일합니다. 변경하려면 새로운 가치를 만들어야합니다.
솔직히 기본적으로 불변 을 만드는 것을 선호하고 성능이 필요하거나 불변에 의미가없는 알고리즘인지 여부에 관계없이 필요한 것으로 입증 된 곳에서 변경 가능하도록 변경 하는 것을 선호 합니다.
답변
코드는 의도를 표현해야합니다. 생성 된 객체를 수정하지 않으려면 수정할 수 없도록하십시오.
불변성은 몇 가지 이점이 있습니다.
-
원저자의 의도가 더 잘 표현됩니다.
다음 코드에서 이름을 수정하면 나중에 어딘가에 예외가 발생한다는 것을 어떻게 알 수 있습니까?
public class Product { public string Name { get; set; } ... }
-
개체가 유효하지 않은 상태로 나타나지 않도록하는 것이 더 쉽습니다.
생성자에서 이것을 제어해야하며 거기에서만 가능합니다. 반면에, 객체를 수정하는 많은 setter와 메소드가있는 경우, 특히 예를 들어 객체가 유효하기 위해 두 필드가 동시에 변경되어야하는 경우에는 이러한 제어가 특히 어려워 질 수 있습니다.
예를 들어, 주소가
null
없거나 GPS 좌표가 아닌 경우 객체가 유효null
하지만 주소와 GPS 좌표가 모두 지정된 경우 유효하지 않습니다. 주소와 GPS 좌표에 세터가 있거나 둘 다 변경할 수있는 경우이를 확인해야한다고 생각할 수 있습니까? -
동시성.
그건 그렇고, 귀하의 경우에는 타사 패키지가 필요하지 않습니다. .NET Framework에는 이미 ReadOnlyDictionary<TKey, TValue>
클래스가 포함되어 있습니다.
답변
불변성을 사용해야하는 단일 스레드 이유가 많이 있습니다. 예를 들어
객체 A는 객체 B를 포함합니다.
외부 코드는 객체 B를 쿼리하여 반환합니다.
이제 세 가지 가능한 상황이 있습니다.
- B는 불변이며 문제 없습니다.
- B는 변경이 가능합니다. 방어적인 사본을 만들어서 돌려주십시오. 성능은 저하되었지만 위험은 없습니다.
- B는 변경 가능합니다.
세 번째 경우, 사용자 코드는 사용자가 수행 한 작업을 인식하지 못하고 객체를 변경할 수 있으며 그렇게함으로써 객체의 내부 데이터를 변경하거나 제어 할 수 있습니다.
답변
불변성은 가비지 수집기의 구현을 크게 단순화 할 수도 있습니다. 에서 GHC의 위키 :
[…] 데이터 불변성으로 인해 많은 임시 데이터가 생성되지만이 가비지를 빠르게 수집하는 데 도움이됩니다. 요점은 불변 데이터가 결코 더 젊은 값을 가리 키지 않는다는 것입니다. 실제로 이전 값을 만들 때 아직 더 어린 값이 존재하지 않으므로 처음부터 가리킬 수 없습니다. 그리고 값은 수정되지 않으므로 나중에 가리킬 수 없습니다. 이것은 불변 데이터의 핵심 속성입니다.
이것은 가비지 수집 (GC)을 크게 단순화합니다. 언제든지 마지막으로 생성 된 값을 스캔하고 동일한 세트에서 가리 키지 않은 값을 해제 할 수 있습니다 (물론 실제 값의 실제 값 계층 구조는 스택에 있습니다). […] 따라서 반 직관적 인 동작이 있습니다. 값의 큰 비율이 가비지입니다. 작동 속도가 빠릅니다. […]
답변
KChaloux가 요약 한 내용을 확장 하면 …
이상적으로는 두 가지 유형의 필드가 있으므로이를 사용하는 두 가지 유형의 코드가 있습니다. 필드는 변경할 수 없으며 코드는 변경 성을 고려할 필요가 없습니다. 또는 필드는 변경 가능하므로 스냅 샷 ( int x = p.x
)을 취 하거나 그러한 변경을 정상적으로 처리 하는 코드를 작성해야합니다 .
필자의 경험에 따르면 대부분의 코드는 두 코드 사이에 있으며 낙관적 코드입니다. 첫 번째 호출 p.x
이 두 번째 호출과 동일한 결과 를 가정 할 때 가변 데이터를 자유롭게 참조 합니다. 그리고 대부분의 경우, 더 이상 사실이 아닌 것을 제외하고는 사실입니다. 죄송합니다.
그래서, 실제로 그 질문을 돌리십시오 : 이것을 변경 가능하게 만드는 이유는 무엇입니까 ?
- 메모리 할당량을 늘리고 있습니까?
- 본질적으로 변하기 쉬운가? (예 : 카운터)
- 수정 자, 수평 노이즈를 저장합니까? (const / final)
- 일부 코드를 더 짧게 / 쉽게 만드나요? (초기 기본값, 이후 덮어 쓰기 가능)
방어 코드를 작성하십니까? 불변성은 복사를 줄여줍니다. 낙관적 인 코드를 작성하십니까? 불변성은 그 이상하고 불가능한 버그의 광기를 살려 줄 것입니다.
답변
불변성의 또 다른 이점은 이러한 불변 객체를 풀로 반올림하는 첫 번째 단계라는 것입니다. 그런 다음 개념적으로 의미 적으로 같은 것을 나타내는 여러 개체를 만들지 않도록 관리 할 수 있습니다. 좋은 예는 Java의 문자열입니다.
언어학에서 잘 알려진 현상으로, 몇 단어가 많이 나타나고 다른 상황에서도 나타날 수 있습니다. 따라서 여러 String
객체 를 만드는 대신 하나의 불변을 사용할 수 있습니다. 그러나 이러한 불변 개체를 처리하려면 풀 관리자를 유지해야합니다.
이렇게하면 많은 메모리가 절약됩니다. 이 기사도 읽어 볼만한 흥미로운 기사입니다 :
http://en.wikipedia.org/wiki/Zipf%27s_law
답변
Java, C # 및 기타 유사한 언어에서 클래스 유형 필드는 객체를 식별하거나 해당 객체의 값 또는 상태를 캡슐화하는 데 사용될 수 있지만 언어는 이러한 사용법을 구분하지 않습니다. 클래스 객체 George
의 유형이 필드 라고 가정합니다 char[] chars;
. 이 필드는 다음 중 하나의 문자 순서를 캡슐화 할 수 있습니다.
-
절대 수정되거나 수정 될 수 있지만 외부 참조가 존재할 수있는 코드에는 노출되지 않는 배열입니다.
-
외부 참조는 없지만 George가 자유롭게 수정할 수있는 배열입니다.
-
George가 소유하지만 George의 현재 상태를 나타낼 것으로 예상되는 외부보기가있을 수있는 배열입니다.
또한 변수는 문자 시퀀스를 캡슐화하는 대신 라이브 뷰를 다른 객체가 소유 한 문자 시퀀스로 캡슐화 할 수 있습니다.
경우 chars
현재 문자 순서 [바람] 캡슐화, 조지가 원하는 chars
문자 시퀀스 [지팡이]을 캡슐화하는 조지가 할 수있는 일이 될 것입니다 :
A. [wand] 문자를 포함하는 새로운 배열을 구성 chars
하고 이전 배열이 아닌 해당 배열을 식별하도록 변경 하십시오.
B. 어떻게 든 기존 문자 배열을 식별하여 항상 문자 [원치]를 보유 chars
하고 이전 배열이 아닌 해당 배열을 식별하도록 변경 합니다.
C.에 의해 확인 된 배열의 두 번째 문자 변경 chars
에를 a
.
경우 1, (A) 및 (B)는 원하는 결과를 얻는 안전한 방법입니다. (2), (A) 및 (C)가 안전하지만 (B)는 [즉시 문제를 일으키지 않지만, George가 배열의 소유권을 가지고 있다고 가정하므로 배열을 소유한다고 가정하므로 배열을 마음대로 바꿀 수 있습니다]. (3)의 경우, 선택 (A)와 (B)는 외부 관점을 어기므로 선택 (C) 만 맞습니다. 따라서, 필드에 의해 캡슐화 된 문자 시퀀스를 수정하는 방법을 알기 위해서는 필드의 의미 유형이 무엇인지 알아야합니다.
char[]
잠재적으로 변경 가능한 문자 시퀀스를 캡슐화하는 type 필드를 사용하는 대신 코드가 String
불변 문자 시퀀스를 캡슐화하는 type을 사용 하면 위의 모든 문제가 사라집니다. 모든 유형의 필드는 String
변경되지 않는 공유 가능 객체를 사용하여 일련의 문자를 캡슐화합니다. 따라서 필드 유형이String
“바람”을 캡슐화하는 “바람”을 캡슐화하는 유일한 방법은 “바람”을 보유하는 다른 객체를 식별하는 것입니다. 코드가 객체에 대한 유일한 참조를 보유하는 경우 객체를 변경하는 것이 새 객체를 만드는 것보다 효율적일 수 있지만 클래스가 변경 될 때마다 값을 캡슐화 할 수있는 여러 가지 방법을 구별해야합니다. 개인적으로 나는 이것을 위해 Apps Hungarian을 사용해야한다고 생각합니다 ( char[]
유형 시스템이 동일한 유형으로 간주하지만 (정확하게 Apps Hungarian이 빛나는 일종의 상황)) 이러한 모호성을 피하는 가장 쉬운 방법은 아닙니다. 값을 한 방향으로 만 캡슐화하는 불변 유형을 디자인하는 것입니다.