답변:
문자열은 값이 크지 않으므로 힙에 저장해야하기 때문에 값 유형이 아닙니다. 값 유형은 (아직 CLR의 모든 구현에서) 스택에 저장됩니다. 스택 할당 문자열은 모든 종류의 것들을 망칠 것입니다 : 스택은 32 비트의 경우 1MB, 64 비트의 경우 4MB입니다. 각 문자열을 상자에 넣어 복사해야하며 벌금을 내야하며 인턴 문자열을 사용할 수 없으며 메모리 사용이 필요합니다 풍선 등
(편집 : 구현 세부 사항 인 값 유형 스토리지에 대한 설명이 추가되어 System.ValueType에서 상속되지 않는 값 sematics가있는 유형이 있습니다. 감사합니다. 벤)
String
가변 크기가 아닙니다. 추가하면 실제로 다른 String
객체를 만들어 새 메모리를 할당합니다.
Int32
는 항상 4 바이트이므로 문자열 변수를 정의 할 때마다 컴파일러에서 4 바이트를 할당합니다. int
변수에 변수 가있을 때 (값 유형 인 경우) 컴파일러는 얼마나 많은 메모리를 할당해야 합니까? 당시 값이 아직 할당되지 않았 음을 이해하십시오.
Int32
는 항상 4 바이트이므로 int
변수 를 정의 할 때마다 컴파일러가 4 바이트를 할당 합니다. string
변수에 변수 가있을 때 (값 유형 인 경우) 컴파일러는 얼마나 많은 메모리를 할당해야 합니까? 당시 값이 아직 할당되지 않았 음을 이해하십시오.
값 유형 인 경우 성능 (공간 및 시간!)이 끔찍하고 메소드 등에서 전달되거나 리턴 될 때마다 값을 복사해야하므로 값 유형이 아닙니다.
세계를 제정신으로 유지하기위한 가치 의미론이 있습니다. 코드를 작성하는 것이 얼마나 어려운지 상상할 수 있습니까?
string s = "hello";
string t = "hello";
bool b = (s == t);
설정 b
으로 false
? 모든 응용 프로그램에서 코딩이 얼마나 어려운지를 상상해보십시오.
new String("foo");
다른 new String("foo")
참조가 동일한 참조로 평가할 수 있기 때문에 new
운영자 가 기대하는 것이 아닙니다 . (또는 참고 문헌을 비교하고 싶은 경우를 말씀해 주시겠습니까?)
ReferenceEquals(x, y)
는 빠른 테스트이며 즉시 0을 반환 할 수 있으며 null 테스트와 혼합해도 더 이상 작업이 추가되지 않습니다.
string
이 null 참조가 아닌 빈 문자열 (.net 이전 시스템과 동일)로 작동 할 수 있습니다. 사실, 내 자신의 환경 설정 값 형식이하는 것입니다 String
참조 형을 포함 NullableString
전자가에 디폴트 값에 해당 갖는, String.Empty
그리고의 기본을 갖는 후자를 null
, 특별 권투와 / 언 박싱 규칙 (예 : 그 권투 default- valued NullableString
는 String.Empty
)에 대한 참조를 생성합니다 .
참조 유형과 값 유형의 구별은 기본적으로 언어 디자인에서 성능 상충 관계입니다. 참조 유형은 힙에 작성되므로 구성 및 소멸 및 가비지 콜렉션에 약간의 오버 헤드가 있습니다. 반면에 값 유형은 전체 객체가 포인터가 아니라 복사되기 때문에 메소드 호출에 대한 오버 헤드가 있습니다 (데이터 크기가 포인터보다 큰 경우). 문자열은 포인터 크기보다 훨씬 클 수 있으며 일반적으로 참조 유형으로 설계되었습니다. 또한 Servy가 지적했듯이 값 유형의 크기는 컴파일 타임에 알고 있어야하며 이는 항상 문자열의 경우는 아닙니다.
가변성의 문제는 별도의 문제입니다. 참조 유형과 값 유형은 모두 변경 가능하거나 변경 불가능할 수 있습니다. 가변 값 형식의 의미가 혼동 될 수 있으므로 일반적으로 값 형식은 변경할 수 없습니다.
참조 유형은 일반적으로 변경 가능하지만 의미가있는 경우 변경 불가능하도록 설계 할 수 있습니다. 문자열은 특정 최적화가 가능하기 때문에 불변으로 정의됩니다. 예를 들어, 동일한 프로그램에서 동일한 문자열 리터럴이 여러 번 발생하는 경우 (일반적인 경우) 컴파일러는 동일한 객체를 재사용 할 수 있습니다.
그렇다면 "=="문자열을 텍스트로 비교하기 위해 오버로드되는 이유는 무엇입니까? 가장 유용한 의미론이기 때문입니다. 두 문자열이 텍스트와 같으면 최적화로 인해 동일한 객체 참조 일 수도 있고 아닐 수도 있습니다. 따라서 참조를 비교하는 것은 무의미하지만 텍스트를 비교하는 것은 거의 항상 원하는 것입니다.
보다 일반적으로 말하면, Strings는 value semantics 라고 불립니다 . 이는 C # 고유의 구현 세부 사항 인 값 형식보다 일반적인 개념입니다. 값 형식에는 값 의미가 있지만 참조 형식에는 값 의미가있을 수도 있습니다. 유형에 값 의미가있는 경우 기본 구현이 참조 유형 또는 값 유형인지 실제로 알 수 없으므로 구현 세부 사항을 고려할 수 있습니다.
string
유형에는 고정 크기의 char 버퍼가 있어야하며 이는 제한적이고 매우 비효율적입니다.
이것은 오래된 질문에 대한 늦은 대답이지만 다른 모든 대답에는 요점이 없습니다. 2005 년 .NET 2.0까지 .NET에는 제네릭이 없었습니다.
String
때문에 대신 값 형식의 참조 형식은 마이크로 소프트가 그 문자열이 제네릭이 아닌 컬렉션에 가장 효율적인 방법으로 저장 될 수 있도록하는 것이 매우 중요했다 같은, System.Collections.ArrayList
.
제네릭이 아닌 컬렉션에 값 형식을 저장하려면 object
boxing이라는 형식으로 특별한 변환이 필요합니다 . CLR은 값 유형을 상자에 넣을 때 값을 a 안에 System.Object
넣고 관리되는 힙에 저장합니다.
컬렉션에서 값을 읽으려면 unboxing이라고하는 역 연산이 필요합니다.
권투와 unboxing 모두 무시할 수없는 비용이 있습니다 : 권투는 추가 할당이 필요하고 unboxing은 유형 검사가 필요합니다.
일부 답변 string
은 크기가 가변적이므로 값 유형으로 구현 될 수 없다고 잘못 주장 합니다. 실제로 작은 문자열 최적화 전략을 사용하여 문자열을 고정 길이 데이터 구조로 구현하는 것은 쉽습니다. 문자열은 외부 버퍼에 대한 포인터로 저장되는 큰 문자열을 제외하고는 유니 코드 문자 시퀀스로 메모리에 직접 저장됩니다. 두 표현은 동일한 고정 길이, 즉 포인터의 크기를 갖도록 설계 될 수 있습니다.
제네릭이 첫날부터 존재했다면 문자열을 값 유형으로 사용하는 것이 더 간단한 의미, 더 나은 메모리 사용 및 더 나은 캐시 위치를 가진 더 나은 솔루션 일 것입니다. List<string>
함유 단지 작은 문자열은 메모리의 하나의 연속 블록 수 있었다.
string
단지 크기와에 대한 포인터를 포함 char
어쨌든 배열을, 그래서 그것은 "큰 값 유형"하지 않을 것입니다. 그러나 이것이이 디자인 결정에 대한 단순하고 관련이있는 이유입니다. 감사!
문자열 만이 변경 불가능한 참조 유형일뿐만 아니라 멀티 캐스트 델리게이트도. 그래서 작성하는 것이 안전합니다
protected void OnMyEventHandler()
{
delegate handler = this.MyEventHandler;
if (null != handler)
{
handler(this, new EventArgs());
}
}
문자열을 사용하고 메모리를 할당하는 가장 안전한 방법이기 때문에 문자열을 변경할 수 없다고 가정합니다. 왜 그들은 가치 유형이 아닌가? 이전 작성자는 스택 크기 등에 대해 맞습니다. 또한 프로그램에서 동일한 상수 문자열을 사용할 때 참조 유형을 문자열로 지정하면 어셈블리 크기를 절약 할 수 있습니다. 정의하면
string s1 = "my string";
//some code here
string s2 = "my string";
"my string"상수의 두 인스턴스가 모두 어셈블리에 한 번만 할당 될 수 있습니다.
일반적인 참조 유형과 같은 문자열을 관리하려면 문자열을 새 StringBuilder (string s) 안에 넣습니다. 또는 MemoryStreams를 사용하십시오.
함수에 거대한 문자열이 전달 될 것으로 예상되는 라이브러리를 작성하려면 매개 변수를 StringBuilder 또는 Stream으로 정의하십시오.
string
참조 유형을 어떻게 알 수 있습니까? 그것이 어떻게 구현되는지 중요하지 않다. C #의 문자열은 정확하게 변경할 수 없으므로이 문제에 대해 걱정할 필요가 없습니다.
실제로 문자열은 값 유형과 거의 유사하지 않습니다. 우선 모든 값 유형을 변경할 수있는 것은 아니며 Int32의 값을 원하는대로 변경할 수 있으며 스택의 주소는 여전히 동일합니다.
문자열은 아주 좋은 이유 때문에 불변이며 참조 유형과는 관련이 없지만 메모리 관리와는 많은 관련이 있습니다. 문자열 크기가 변경 될 때 관리되는 힙에서 물건을 옮기는 것보다 새 객체를 만드는 것이 더 효율적입니다. 나는 당신이 가치 / 참조 유형과 불변의 객체 개념을 함께 혼합한다고 생각합니다.
"=="까지 : "=="는 연산자 오버로드이며 문자열로 작업 할 때 프레임 워크를 더 유용하게 만드는 매우 좋은 이유로 다시 구현되었습니다.
문자열이 문자 배열로 구성된 것처럼 간단하지 않습니다. 문자열을 문자형 배열로 본다 []. 따라서 참조 메모리 위치가 스택에 저장되고 힙에서 어레이의 메모리 위치 시작을 가리 키기 때문에 힙에 있습니다. 문자열 크기는 힙에 할당되기 전에 알 수 없습니다.
그렇기 때문에 문자열이 같은 크기 일지라도 문자열을 변경할 때 컴파일러는이를 알지 못하고 새로운 배열을 할당하고 배열의 위치에 문자를 할당해야하기 때문에 문자열을 변경할 수 없습니다. 언어가 메모리를 즉시 할당하지 않도록 보호하는 방법으로 문자열을 생각하는 것이 합리적입니다 (프로그래밍과 같은 C 읽기)
또 하나의 신비한 다운 투표권을 얻을 위험이 있습니다 ... 많은 사람들이 가치 유형과 기본 유형과 관련하여 스택과 메모리를 언급한다는 사실은 마이크로 프로세서의 레지스터에 맞아야하기 때문입니다. 레지스터가 가지고있는 것보다 더 많은 비트를 필요로하는 경우 스택에 무언가를 밀거나 팝할 수 없습니다.
부동 소수점 기본 유형은 80 비트 너비의 FPU에 의해 처리됩니다.
이것은 프리미티브 유형의 정의를 난독 화하기 위해 OOP 언어가 있기 오래 전에 결정되었으며 값 유형은 OOP 언어를 위해 특별히 작성된 용어라고 가정합니다.
is
테스트 제외)이므로 "역사적인 이유"일 것입니다. 불변 개체를 물리적으로 복사 할 필요가 없기 때문에 복사 성능이 이유가 될 수 없습니다. 이제 실제로is
검사 (또는 유사한 제약 조건)를 사용하는 코드를 중단하지 않고 변경할 수 없습니다 .