동시성이 없을 때 불변성이 매우 가치가 있습니까?


53

스레드 안전성은 항상 불변 유형 및 특히 컬렉션을 사용하는 주요 이점으로 항상 언급되는 것처럼 보입니다.

메서드가 문자열 사전 (C #에서는 변경할 수 없음)을 수정하지 않도록하려는 상황이 있습니다. 가능한 한 많이 제한하고 싶습니다.

그러나 새 패키지 (Microsoft Immutable Collections)에 종속성을 추가하는 것이 가치가 있는지 확실하지 않습니다. 성능도 큰 문제가 아닙니다.

따라서 어려운 성능 요구 사항이없고 스레드 안전 문제가없는 경우 불변 컬렉션이 강력하게 권장 되는지 여부가 제 질문입니다 . 값 의미론 (내 예제 ​​에서처럼)도 요구 사항 일 수도 있고 아닐 수도 있습니다.


1
동시 수정은 스레드를 의미 할 필요는 없습니다. ConcurrentModificationException일반적으로 동일한 스레드 foreach가 동일한 컬렉션 의 루프 본문에서 동일한 스레드의 컬렉션을 변경함으로써 발생 하는 적절한 이름 을 찾으십시오 .

1
나는 당신이 틀리지 않았 음을 의미하지만, OP가 요구하는 것과 다릅니다. 열거하는 동안 수정이 허용되지 않기 때문에 해당 예외가 발생합니다. 예를 들어 ConcurrentDictionary를 사용하면 여전히이 오류가 발생합니다.
edthethird

13
아마도 당신은 자신에게 반대 질문을해야합니다 : 언제 변이가 가치가 있습니까?
Giorgio

2
Java에서 변경 가능성에 영향을 미치 hashCode()거나 equals(Object)변경되는 경우 사용시 오류가 발생할 수 있습니다 Collections(예 : HashSet오브젝트가 "버킷"에 저장된 경우 및 변경 후 다른 오브젝트로 이동해야 함).
SJuan76

2
@ DavorŽdralo 고급 언어의 추상화가 진행되는 한 퍼 베이트 불변성은 다소 길들입니다. 이것은 "임시 값"을 생성하고 자동으로 버리는 매우 일반적인 추상화 (C에도 있음)의 자연스러운 확장 일뿐입니다. 아마도 당신은 CPU를 사용하는 비효율적 인 방법이라고 말하지만 그 주장에도 결함이 있습니다. 불변 데이터를

답변:


101

불변성은 나중에 코드를 읽을 때 정신적으로 추적해야하는 정보의 양을 단순화합니다 . 변경 가능한 변수, 특히 변경 가능한 클래스 멤버의 경우 디버거를 사용하여 코드를 실행하지 않고 읽고있는 특정 라인에서 어떤 상태에 있는지 알기가 매우 어렵습니다. 변경 불가능한 데이터는 추론하기 쉽습니다 . 항상 동일합니다. 변경하려면 새로운 가치를 만들어야합니다.

솔직히 기본적으로 불변 만드는 것을 선호하고 성능이 필요하거나 불변에 의미가없는 알고리즘인지 여부에 관계없이 필요한 것으로 입증 된 곳에서 변경 가능하도록 변경 하는 것을 선호 합니다.


23
+1 동시성은 동시 돌연변이이지만, 시간이 지남에 따라 확산되는 돌연변이는 다음과 같이 추론하기 어려울 수 있습니다.
guillaume31

20
이를 확장하기 위해 : 가변 변수에 의존하는 함수는 가변 변수의 현재 값인 추가 숨겨진 인수를 취하는 것으로 생각할 수 있습니다. 가변 변수를 변경하는 모든 함수는 추가 반환 값, 즉 가변 상태의 새로운 값을 생성하는 것으로 생각할 수 있습니다. 코드를 볼 때 변경 가능한 상태에 의존하는지 또는 변경 가능한 상태인지 여부를 알지 못하므로 변경 사항을 찾아서 정신적으로 추적해야합니다. 이것은 또한 변경 가능한 상태를 공유하는 두 코드 조각 사이의 연결을 유도하며 연결이 잘못되었습니다.
Doval

29
@Mehrdad 사람들은 또한 수십 년 동안 대규모 프로그램을 집결했습니다. 그런 다음 우리는 C.의 수십 년 한
Doval

11
@Mehrdad 개체가 큰 경우 전체 개체를 복사하는 것이 좋은 옵션이 아닙니다. 나는 왜 개선과 관련된 규모의 순서가 중요한지 알지 못한다. 단순히 3 자리 숫자 개선이 아니기 때문에 생산성에서 20 % 개선 (참고 : 임의의 숫자)을 줄이겠습니까? 불변성은 정상적인 기본값입니다 . 당신 그것으로부터 벗어날 있지만, 이유가 필요합니다.
Doval

9
@Giorgio Scala를 사용하면 값을 변경 해야하는 경우가 얼마나 드문 지 깨달았습니다. 해당 언어를 사용할 때마다 모든 것을 a로 만들고 val매우 드문 경우에만 무언가를로 바꿔야한다는 것을 알게됩니다 var. 특정 언어로 정의한 많은 '변수'는 ​​계산 결과를 저장하는 값을 보유하기 위해 존재하며 업데이트 할 필요가 없습니다.
KChaloux

22

코드는 의도를 표현해야합니다. 생성 된 객체를 수정하지 않으려면 수정할 수 없도록하십시오.

불변성은 몇 가지 이점이 있습니다.

  • 원저자의 의도가 더 잘 표현됩니다.

    다음 코드에서 이름을 수정하면 나중에 어딘가에 예외가 발생한다는 것을 어떻게 알 수 있습니까?

    public class Product
    {
        public string Name { get; set; }
    
        ...
    }
    
  • 개체가 유효하지 않은 상태로 나타나지 않도록하는 것이 더 쉽습니다.

    생성자에서 이것을 제어해야하며 거기에서만 가능합니다. 반면에, 객체를 수정하는 많은 setter와 메소드가있는 경우, 특히 예를 들어 객체가 유효하기 위해 두 필드가 동시에 변경되어야하는 경우에는 이러한 제어가 특히 어려워 질 수 있습니다.

    예를 들어, 주소가 null 없거나 GPS 좌표가 아닌 경우 객체가 유효 null하지만 주소와 GPS 좌표가 모두 지정된 경우 유효하지 않습니다. 주소와 GPS 좌표에 세터가 있거나 둘 다 변경할 수있는 경우이를 확인해야한다고 생각할 수 있습니까?

  • 동시성.

그건 그렇고, 귀하의 경우에는 타사 패키지가 필요하지 않습니다. .NET Framework에는 이미 ReadOnlyDictionary<TKey, TValue>클래스가 포함되어 있습니다.


1
+1, 특히 "생성자 안에서만 제어해야합니다." IMO는 큰 장점입니다.
Giorgio

10
또 다른 이점 : 객체 복사는 무료입니다. 그냥 포인터.
Robert Grant

1
@MainMa 답장을 보내 주셔서 감사하지만 ReadOnlyDictionary가 이해하는 한 다른 사람이 기본 사전을 변경하지 않을 것이라고 보장하지 않습니다 (동시성이 없어도 메서드가 속한 객체 내에서 원래 사전에 대한 참조를 저장하려고 할 수 있음) 나중에 사용하기 위해). ReadOnlyDictionary는 이상한 네임 스페이스 (System.Collections.ObjectModel)에서도 선언됩니다.
Den

2
@Den : 그것은 나의 애완 동물 친구 중 하나와 관련이 있습니다 : "읽기 전용"과 "불변의"를 동의어로 간주하는 사람들. 개체가 읽기 전용 래퍼로 캡슐화되고 다른 참조가 없거나 유니버스의 어느 곳에도 유지되지 않으면 개체를 래핑하면 개체가 변경 되지 않으며 래퍼에 대한 참조가 속기의 상태를 캡슐화하는 데 사용됩니다. 그 안에 들어있는 물건. 그러나 코드가 그러한 경우인지 확인할 수있는 메커니즘은 없습니다. 반대로 래퍼는 래핑 된 개체의 유형을 숨기고 불변 개체를 래핑하기 때문에 ...
supercat

2
... 결과 래퍼가 변경 불가능한 것으로 간주되는지 코드에서 알 수 없게합니다.
supercat

13

불변성을 사용해야하는 단일 스레드 이유가 많이 있습니다. 예를 들어

객체 A는 객체 B를 포함합니다.

외부 코드는 객체 B를 쿼리하여 반환합니다.

이제 세 가지 가능한 상황이 있습니다.

  1. B는 불변이며 문제 없습니다.
  2. B는 변경이 가능합니다. 방어적인 사본을 만들어서 돌려주십시오. 성능은 저하되었지만 위험은 없습니다.
  3. B는 변경 가능합니다.

세 번째 경우, 사용자 코드는 사용자가 수행 한 작업을 인식하지 못하고 객체를 변경할 수 있으며 그렇게함으로써 객체의 내부 데이터를 변경하거나 제어 할 수 있습니다.


9

불변성은 가비지 수집기의 구현을 크게 단순화 할 수도 있습니다. 에서 GHC의 위키 :

[...] 데이터 불변성으로 인해 많은 임시 데이터가 생성되지만이 가비지를 빠르게 수집하는 데 도움이됩니다. 요점은 불변 데이터가 결코 더 젊은 값을 가리 키지 않는다는 것입니다. 실제로 이전 값을 만들 때 아직 더 어린 값이 존재하지 않으므로 처음부터 가리킬 수 없습니다. 그리고 값은 수정되지 않으므로 나중에 가리킬 수 없습니다. 이것은 불변 데이터의 핵심 속성입니다.

이것은 가비지 수집 (GC)을 크게 단순화합니다. 언제든지 마지막으로 생성 된 값을 스캔하고 동일한 세트에서 가리 키지 않은 값을 해제 할 수 있습니다 (물론 실제 값의 실제 값 계층 구조는 스택에 있습니다). [...] 따라서 반 직관적 인 동작이 있습니다. 값의 큰 비율이 가비지입니다. 작동 속도가 빠릅니다. [...]


5

KChaloux가 요약 한 내용을 확장 하면 ...

이상적으로는 두 가지 유형의 필드가 있으므로이를 사용하는 두 가지 유형의 코드가 있습니다. 필드는 변경할 수 없으며 코드는 변경 성을 고려할 필요가 없습니다. 또는 필드는 변경 가능하므로 스냅 샷 ( int x = p.x)을 취 하거나 그러한 변경을 정상적으로 처리 하는 코드를 작성해야합니다 .

필자의 경험에 따르면 대부분의 코드는 두 코드 사이에 있으며 낙관적 코드입니다. 첫 번째 호출 p.x이 두 번째 호출과 동일한 결과 를 가정 할 때 가변 데이터를 자유롭게 참조 합니다. 그리고 대부분의 경우, 더 이상 사실이 아닌 것을 제외하고는 사실입니다. 죄송합니다.

그래서, 실제로 그 질문을 돌리십시오 : 이것을 변경 가능하게 만드는 이유는 무엇입니까 ?

  • 메모리 할당량을 늘리고 있습니까?
  • 본질적으로 변하기 쉬운가? (예 : 카운터)
  • 수정 자, 수평 노이즈를 저장합니까? (const / final)
  • 일부 코드를 더 짧게 / 쉽게 만드나요? (초기 기본값, 이후 덮어 쓰기 가능)

방어 코드를 작성하십니까? 불변성은 복사를 줄여줍니다. 낙관적 인 코드를 작성하십니까? 불변성은 그 이상하고 불가능한 버그의 광기를 살려 줄 것입니다.


3

불변성의 또 다른 이점은 이러한 불변 객체를 풀로 반올림하는 첫 번째 단계라는 것입니다. 그런 다음 개념적으로 의미 적으로 같은 것을 나타내는 여러 개체를 만들지 않도록 관리 할 수 ​​있습니다. 좋은 예는 Java의 문자열입니다.

언어학에서 잘 알려진 현상으로, 몇 단어가 많이 나타나고 다른 상황에서도 나타날 수 있습니다. 따라서 여러 String객체 를 만드는 대신 하나의 불변을 사용할 수 있습니다. 그러나 이러한 불변 개체를 처리하려면 풀 관리자를 유지해야합니다.

이렇게하면 많은 메모리가 절약됩니다. 이 기사도 읽어 볼만한 흥미로운 기사입니다 : http://en.wikipedia.org/wiki/Zipf%27s_law


1

Java, C # 및 기타 유사한 언어에서 클래스 유형 필드는 객체를 식별하거나 해당 객체의 값 또는 상태를 캡슐화하는 데 사용될 수 있지만 언어는 이러한 사용법을 구분하지 않습니다. 클래스 객체 George의 유형이 필드 라고 가정합니다 char[] chars;. 이 필드는 다음 중 하나의 문자 순서를 캡슐화 할 수 있습니다.

  1. 절대 수정되거나 수정 될 수 있지만 외부 참조가 존재할 수있는 코드에는 노출되지 않는 배열입니다.

  2. 외부 참조는 없지만 George가 자유롭게 수정할 수있는 배열입니다.

  3. 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이 빛나는 일종의 상황)) 이러한 모호성을 피하는 가장 쉬운 방법은 아닙니다. 값을 한 방향으로 만 캡슐화하는 불변 유형을 디자인하는 것입니다.


합리적인 답변처럼 보이지만 이해하고 이해하기가 조금 어렵습니다.
Den

1

여기에 좋은 예가 있지만 불변성이 많은 도움이되는 개인적인 것들로 뛰어 들기를 원했습니다. 필자의 경우 필자는 중복 된 읽기 및 쓰기와 함께 병렬로 코드를 자신있게 실행할 수 있고 경쟁 조건에 대해 걱정할 필요가 없다는 희망을 가지고 불변의 동시 데이터 구조를 설계하기 시작했습니다. John Carmack이 그런 아이디어에 대해 이야기 할 때 영감을 준 이야기가있었습니다. 다음과 같이 구현하는 것은 매우 기본적인 구조이며 매우 사소합니다.

여기에 이미지 설명을 입력하십시오

물론 일정 시간 내에 요소를 제거하고 회수 가능한 구멍을 남기고 주어진 불변 인스턴스에 대해 비어 있고 잠재적으로 해제되면 블록이 무시되도록하는 것처럼 종과 휘파람이 몇 개 더 있습니다. 그러나 기본적으로 구조를 수정하려면 "일시적인"버전을 수정하고 변경 사항을 원자 적으로 적용하여 이전 버전에 닿지 않는 새로운 불변 ​​사본을 얻습니다. 새로운 버전은 블록의 새 사본 만 만듭니다. 다른 사람을 얕게 복사하고 참조하는 동안 고유해야합니다.

그러나, 나는 그것을 찾지 못했습니다 멀티 스레딩 목적에 유용합니다. 결국, 물리 시스템이 플레이어가 세계에서 요소를 움직이려고 시도하는 동안 물리를 동시에 적용하는 개념적 문제가 여전히 있습니다. 플레이어가 변형 한 것과 물리 시스템이 변형 한 변형 된 데이터의 어떤 불변 사본이 있습니까? 그래서 나는 똑똑한 방식으로 잠그고 버퍼의 동일한 섹션에 겹치는 읽기 및 쓰기를 금지하여 변경 스레드를 피하는 것을 제외 하고는이 기본 개념 문제에 대한 훌륭하고 간단한 해결책을 찾지 못했습니다. John Carmack이 그의 게임에서 어떻게 해결할 수 있을지 알아 낸 것 같습니다. 적어도 그는 벌레 차를 열지 않고도 해결책을 거의 볼 수있는 것처럼 그것에 대해 이야기합니다. 나는 그런 점에서 그에게까지 도달하지 못했습니다. 불변 주변의 모든 것을 병렬화하려고하면 끝없는 디자인 질문이 있습니다. 나는 그의 노력의 대부분이 그가 튀어 나온 아이디어로 시작했기 때문에 그의 두뇌를 따기 위해 하루를 보낼 수 있기를 바랍니다.

그럼에도 불구하고, 나는 다른 영역 에서이 불변의 데이터 구조의 엄청난 가치를 발견했습니다 . 나는 심지어 그것을 정말로 이상한 이미지를 저장하기 위해 지금 사용하고 무작위 액세스는 더 많은 지시 ( and포인터 간접 레이어와 함께 오른쪽 시프트와 비트 단위 )를 필요로하지만, 아래의 이점을 다룰 것입니다.

시스템 취소

내가이 혜택을 얻는 가장 즉각적인 장소 중 하나는 실행 취소 시스템이었습니다. 실행 취소 시스템 코드는 내가 작업했던 제품뿐만 아니라 경쟁 제품 (실행 취소 시스템도 결함이 있음)에서 가장 오류가 발생하기 쉬운 영역 중 하나 (비주얼 FX 산업) 중 하나였습니다. 실행 취소 및 재실행에 대해 걱정해야 할 데이터 유형 (속성 시스템, 메시 데이터 변경, 서로 교환하는 것과 같이 속성 기반이 아닌 셰이더 변경, 자식의 부모 변경, 이미지 / 텍스처 변경, 등 등).

따라서 실행 취소 코드의 양은 엄청 났으며, 실행 취소 시스템이 상태 변경을 기록해야하는 시스템을 구현하는 코드의 양과 비교할 때 종종 많았습니다. 이 데이터 구조에 기대어 실행 취소 시스템을 다음과 같이 만들 수있었습니다.

on user operation:
    copy entire application state to undo entry
    perform operation

on undo/redo:
    swap application state with undo entry

일반적으로 위의 코드는 장면 데이터가 기가 바이트에 걸쳐 전체를 복사 할 때 엄청나게 비효율적입니다. 그러나이 데이터 구조는 변경되지 않은 내용 만 얕게 복사하기 때문에 실제로 전체 응용 프로그램 상태의 변경 불가능한 복사본을 저장할 수있을 정도로 저렴했습니다. 이제 위 코드처럼 쉽게 실행 취소 시스템을 구현하고 변경 불가능한 데이터 구조를 사용하여 애플리케이션 상태의 변경되지 않은 부분을보다 저렴하고 저렴하게 복사 할 수 있습니다. 이 데이터 구조를 사용하기 시작한 이후, 모든 개인 프로젝트는이 간단한 패턴을 사용하여 시스템을 취소합니다.

이제 여기에는 여전히 약간의 오버 헤드가 있습니다. 마지막으로 측정 한 내용을 변경하지 않고 전체 응용 프로그램 상태를 얕게 복사하기 위해 약 10KB가 측정되었습니다. 장면이 계층 구조로 정렬되어 있기 때문에 장면 복잡성과 무관합니다. 따라서 루트 아래에 아무것도 없으면 루트 만 아래로 내려 가지 않고 얕게 복사됩니다). 델타 만 저장하는 실행 취소 시스템에 필요한 0 바이트와는 거리가 멀습니다. 그러나 작업 당 10 킬로바이트의 실행 취소 오버 헤드는 여전히 100 개의 사용자 작업 당 메가 바이트에 불과합니다. 또한 필요할 경우 앞으로도 계속해서 더 이상 스쿼시 할 수 있습니다.

예외 안전

복잡한 응용 프로그램의 예외 안전은 사소한 문제가 아닙니다. 그러나 응용 프로그램 상태가 변경 불가능하고 임시 객체 만 사용하여 원자 변경 트랜잭션을 커밋하려고하면 코드의 일부가 throw되면 새로운 불변 ​​복사를 제공하기 전에 일시적이 사라지기 때문에 본질적으로 예외 안전합니다 . 따라서 복잡한 C ++ 코드베이스에서 항상 찾아낸 가장 어려운 것들 중 하나를 사소하게 만듭니다.

너무 많은 사람들이 종종 C ++에서 RAII 준수 리소스를 사용하고 예외 안전에 충분하다고 생각합니다. 함수가 일반적으로 해당 범위의 로컬 상태 이외의 상태에 부작용을 일으킬 수 있기 때문에 종종 그렇지 않습니다. 이러한 경우 일반적으로 스코프 가드 및 정교한 롤백 로직을 다루어야합니다. 이 데이터 구조로 인해 함수가 부작용을 일으키지 않기 때문에 종종 신경 쓰지 않아도됩니다. 그들은 응용 프로그램의 상태를 변환하는 대신 변환 된 불변의 응용 프로그램 상태 사본을 반환합니다.

비파괴 편집

여기에 이미지 설명을 입력하십시오

비파괴적인 편집은 기본적으로 원래 사용자의 데이터를 건드리지 않고 (입력 데이터와 입력 데이터를 건드리지 않고 출력 데이터 만) 레이어링 / 스태킹 / 연결 작업입니다. 일반적으로 Photoshop과 같은 간단한 이미지 응용 프로그램으로 구현하는 것은 쉽지 않으며 많은 작업이 전체 이미지의 모든 픽셀을 변환하기를 원할 수 있기 때문에이 데이터 구조의 이점은 그리 크지 않을 수 있습니다.

그러나 비파괴 메시 편집의 경우 많은 작업에서 종종 메시의 일부만 변형하려고합니다. 하나의 작업으로 정점을 여기로 옮기고 싶을 수도 있습니다. 다른 하나는 일부 다각형을 세분화하려고 할 수도 있습니다. 불변의 데이터 구조는 작은 부분이 변경된 새로운 버전의 메쉬를 반환하기 위해 전체 메쉬의 전체 사본을 만들 필요가 없도록하는 데 도움이됩니다.

부작용 최소화

이러한 구조를 사용하면 성능에 큰 영향을 미치지 않으면 서 부작용을 최소화하는 기능을 쉽게 작성할 수 있습니다. 요즘 약간의 낭비로 보일지라도 부작용을 일으키지 않고 값으로 전체 불변의 데이터 구조를 반환하는 점점 더 많은 함수를 작성하는 것을 발견했습니다.

예를 들어, 일반적으로 여러 위치를 변형하려는 유혹은 매트릭스와 객체 목록을 수용하고 변경 가능한 방식으로 객체를 변형하는 것일 수 있습니다. 요즘 나는 새로운 객체 목록을 반환한다는 것을 알았습니다.

시스템에 부작용을 일으키지 않는 이와 같은 기능이 더 많으면 정확성을 쉽게 추론하고 정확성을 테스트 할 수 있습니다.

싸구려 사본의 장점

어쨌든, 이것은 불변의 데이터 구조 (또는 영구적 인 데이터 구조)에서 가장 많이 사용되는 영역입니다. 또한 처음에는 약간 이상해 불변 트리와 불변 링크 목록 및 불변 해시 테이블을 만들었지 만 시간이 지남에 따라 그다지 많이 사용하지는 못했습니다. 나는 위의 다이어그램에서 chunky 불변 배열 배열 컨테이너를 주로 사용한다는 것을 알았습니다.

나는 여전히 변형 가능 코드로 작업하는 많은 코드를 가지고 있지만 (적어도 저수준 코드에는 실용적 필요가 있음) 주요 응용 프로그램 상태는 불변의 계층 구조이며 불변의 장면에서 불변의 구성 요소로 드릴 다운됩니다. 더 저렴한 구성 요소 중 일부는 여전히 전체로 복사되지만 메쉬 및 이미지와 같은 가장 비싼 구성 요소는 불변 구조를 사용하여 변환해야 할 부분의 일부만 저렴한 복사본을 허용합니다.


0

이미 좋은 답변이 많이 있습니다. 이것은 .NET에 다소 특정한 추가 정보입니다. 오래된 .NET 블로그 게시물을 조사하고 Microsoft Immutable Collections 개발자의 관점에서 다음과 같은 이점을 얻었습니다.

  1. 스냅 샷 시맨틱을 통해 수신자가 절대 변하지 않는 방식으로 컬렉션을 공유 할 수 있습니다.

  2. 다중 스레드 응용 프로그램의 암시 적 스레드 안전성 (컬렉션에 액세스하는 데 필요한 잠금 없음)

  3. 컬렉션 유형을 수락하거나 반환하는 클래스 멤버가 있고 계약에 읽기 전용 의미를 포함시키려는 경우

  4. 기능적인 프로그래밍 친화적.

  5. 원본 컬렉션이 변경되지 않도록하는 동안 열거하는 동안 컬렉션을 수정할 수 있습니다.

  6. 코드가 이미 처리하는 것과 동일한 IReadOnly * 인터페이스를 구현하므로 마이그레이션이 쉽습니다.

누군가가 당신에게 ReadOnlyCollection, IReadOnlyList 또는 IEnumerable을 건네 주면 유일한 보장은 데이터를 변경할 수 없다는 것입니다. 컬렉션을 전달한 사람이 데이터를 변경하지 않을 것이라는 보장은 없습니다. 그러나 종종 변경되지 않을 것이라는 확신이 필요합니다. 이러한 유형은 내용이 변경 될 때이를 알려주는 이벤트를 제공하지 않으며 변경 될 경우 내용을 열거하는 동안 다른 스레드에서 발생할 수 있습니까? 이러한 동작은 응용 프로그램에서 데이터 손상 및 / 또는 임의 예외로 이어질 수 있습니다.

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