문자열 유형의 기본값이 빈 문자열 대신 널인 이유는 무엇입니까?


218

등의 null메소드를 안전하게 적용하기 전에 모든 문자열을 테스트하는 것은 상당히 성가신 ToUpper()일입니다 StartWith().

기본값이 경우 string빈 문자열 있었다, 나는 시험이없는 것, 그리고 내가 좋아하는 다른 값 유형과 일관성을 위해 그것을 느낄 것 int또는 double예를 들어. 또한 Nullable<String>의미가 있습니다.

그렇다면 C # 디자이너 null가 문자열의 기본값 으로 사용하기 로 선택한 이유는 무엇입니까?

참고 : 이것은 이 질문 과 관련 있지만 질문 과 관련하여 대신에 이유에 더 중점을 둡니다.


53
다른 참조 유형에 대해 이것이 문제라고 생각하십니까 ?
Jon Skeet 2012 년

17
@JonSkeet 아니요, 처음에는 문자열이 값 유형이라고 잘못 생각했기 때문입니다.
Marcel

21
@Marcel : 그것에 대해 궁금해하는 꽤 좋은 이유입니다.
TJ Crowder 2018 년

7
@JonSkeet 예. 어 그래. (그러나 당신은 nullable이 아닌 참조 유형 토론에 익숙하지 않습니다 ...)
Konrad Rudolph

7
나는 문자열이 아닌 것으로 예상되는 장소에서 문자열에 어설 션을 사용하면 훨씬 더 좋은 시간을 가질 수 있다고 생각합니다 null(또한 개념적으로 null문자열을 다른 것으로 취급 하고 비우는 것이 좋습니다 ). 빈 문자열은 다른 의미를 전달해야하지만 null 값은 어딘가에 오류의 결과 일 수 있습니다.
diegoreymendez

답변:


312

문자열 유형의 기본값이 빈 문자열 대신 널인 이유는 무엇입니까?

때문에 stringA는 참조 형 과 기본 값은 모든 참조 형식입니다 null.

ToUpper (), StartWith () 등과 같은 메소드를 안전하게 적용하기 전에 모든 문자열을 null로 테스트하는 것은 상당히 성가신 일입니다.

이는 참조 유형의 동작과 일치합니다. 인스턴스 멤버를 호출하기 전에 null 참조를 확인해야합니다.

string의 기본값이 빈 문자열이라면 테스트 할 필요가 없으며 int 또는 double과 같은 다른 값 유형과 더 일관성이 있다고 생각합니다.

이외의 특정 참조 유형 기본값 지정 null은 할 것 일관성을 .

또한 Nullable<String>의미가 있습니다.

Nullable<T>값 유형과 함께 작동합니다. 참고로 사실이다 Nullable원본에 소개되지 않은 .NET 플랫폼 깨진 코드를 많이들이 그 규칙을 변경했다가되었을 것이다, 그래서이. ( 의례의 @jcolebrand )


10
@HenkHolterman One은 많은 것을 구현할 수 있지만 왜 눈에 띄지 않는 불일치를 소개합니까?

4
@delnan- "왜"가 문제였습니다.
Henk Holterman

8
@HenkHolterman 그리고 "Consistency"는 "문자열은 다른 참조 유형과 달리 처리 될 수 있습니다"라는 반박입니다.

6
@delnan : 문자열을 가치 유형으로 취급하고 닷넷에서 2 년 이상 일하는 언어를 연구하고 있기 때문에 Henk에 동의합니다. 나는 그것을 닷넷 의 주요 FLAW로 본다 .
Fabricio Araujo

1
@delnan : String(1) 사용 가능한 기본값을 갖는 값 유형 -ish 행동 및 (2) 불행한 여분의 복싱 간접 레이어 계층화를 제외하고는 본질적 으로. Object. 의 힙 표현 string이 독특 하다는 점을 감안할 때 여분의 권투를 피하기 위해 특별한 처리를하는 것은 그리 오래되지 않았습니다 (실제로 기본이 아닌 권투 동작을 지정할 수 있으면 다른 유형에도 좋습니다).
supercat

40

하비브가 옳습니다. string . 참조 유형 입니다.

그러나 더 중요한 것은 사용할 때마다 확인할 필요가 없습니다null . ArgumentNullException누군가가 함수를 전달하면 아마도null 참조를 .

여기에 문제 NullReferenceException가 있습니다. 어쨌든 .ToUpper()문자열 을 호출하려고하면 프레임 워크가 대신 합니다. null매개 변수가 평가 될 수 있으므로 함수에 전달 된 객체의 속성이나 메서드는 인수를 테스트하더라도이 경우가 여전히 발생할 수 있습니다.null .

존재가 말했다, 빈 문자열 또는 null을 확인하는 것이 할 수있는 일반적인 일이, 그들이 제공하는 그래서 String.IsNullOrEmpty()String.IsNullOrWhiteSpace()단지 이러한 목적을 위해.


30
절대로 NullReferenceException스스로를 버리지 마십시오 ( msdn.microsoft.com/en-us/library/ms173163.aspx ). ArgumentNullException메소드가 null ref를 허용하지 않으면 if 를 던집니다 . 또한 NullRef는 일반적으로 문제를 해결할 때 진단하기가 더 어려운 예외 중 하나이므로 null을 확인하지 않는 것이 좋습니다.
Andy

3
@Andy "NullRef는 일반적으로 진단하기 가장 어려운 예외 중 하나입니다."자료를 기록하면 찾기 및 수정이 매우 쉽습니다 (널 케이스 만 처리).
Louis Kottmann

6
던지기 ArgumentNullException는 매개 변수 이름을 제공 할 수 있다는 추가 이점이 있습니다. 디버깅하는 동안 이것은 err, 초를 절약합니다. 그러나 중요한 초.
코스

2
@DaveMarkle IsNullOrWhitespace도 포함시킬 수 있습니다. msdn.microsoft.com/en-us/library/…
Nathan Koop

1
나는 모든 곳에서 null을 확인하는 것이 엄청난 코드 팽창의 원천이라고 생각합니다. 그것은 추악하고 해키처럼 보이며 일관성을 유지하기가 어렵습니다. 나는 (적어도 C #과 같은 언어에서) 좋은 규칙이 "제작 코드에서 null 키워드를 금지하고 테스트 코드에서 미친 것처럼 사용하는 것"이라고 생각합니다.
sara mar

24

확장 방법을 작성할 수 있습니다 (가치있는 것).

public static string EmptyNull(this string str)
{
    return str ?? "";
}

이제 이것은 안전하게 작동합니다.

string str = null;
string upper = str.EmptyNull().ToUpper();

100
그러나 제발하지 마십시오. 다른 프로그래머가보고 싶어하는 마지막 것은 첫 번째 사람이 예외에 대해 "무서워"했기 때문에 모든 곳에서 .EmptyNull ()을 사용하는 수천 줄의 코드입니다.
Dave Markle

15
@DaveMarkle : 그러나 분명히 OP가 찾고 있었던 것입니다. "ToUpper (), StartWith () 등의 메소드를 안전하게 적용하기 전에 모든 문자열을 null로 테스트하는 것은 상당히 성가신 일입니다."
Tim Schmelter

19
의견은 OP가 아닌 OP에 대한 것입니다. 당신의 대답은 분명히 정확하지만, 이와 같은 기본적인 질문을하는 프로그래머는 실제로 솔루션을 WIDE 연습에 실제로 넣지 않도록 강력히주의해야합니다. 불투명도, 복잡성 증가, 리팩토링 어려움, 확장 방법의 잠재적 남용 및 성능과 같은 답변에서 다루지 않은 많은 장단점이 있습니다. 때로는 (다수의) 정답이 올바른 길이 아니기 때문에 이것이 제가 언급 한 이유입니다.
Dave Markle

5
@Andy : 적절한 널 점검을하지 않는 해결책은 문제에 반창고를 쓰지 않고 널을 올바르게 점검하는 것입니다.
Dave Markle

7
당신이 쓰기의 문제를 통해려고하는 경우에 .EmptyNull(), 왜 단순히 사용하지 (str ?? "")가 필요한 곳에 대신? 즉, @DaveMarkle의 의견에 표현 된 감정에 동의합니다. nullString.Empty는 다른 개념이다, 당신은 반드시 하나의 또 다른와 동일하게 취급 할 수 없다.
CVn

17

C # 6.0에서 다음을 사용할 수도 있습니다.

string myString = null;
string result = myString?.ToUpper();

문자열 결과는 null입니다.


1
정확하게 말하면, c # 6.0부터 IDE 버전은 언어 기능이기 때문에 IDE와 관련이 없습니다.
Stijn Van Antwerpen

3
또 다른 옵션 –public string Name { get; set; } = string.Empty;
Jaja Harris

이것이 무엇입니까? myString? .ToUpper ();
헌터 넬슨

1
이것을 Null 조건 연산자라고합니다. 여기에서 읽을 수 있습니다 msdn.microsoft.com/en-us/magazine/dn802602.aspx
russelrillema

14

빈 문자열과 null은 근본적으로 다릅니다. null은 값이없고 빈 문자열은 비어있는 값입니다.

변수의 "값"에 대해 가정하는 프로그래밍 언어 (이 경우 빈 문자열)는 null 참조 문제를 일으키지 않는 다른 값으로 문자열을 시작하는 것만 큼 좋습니다.

또한 해당 문자열 변수의 핸들을 응용 프로그램의 다른 부분으로 전달하면 의도적으로 빈 값을 전달했는지 또는 해당 변수의 값을 채우는 것을 잊었는지 여부를 해당 코드에서 확인할 방법이 없습니다.

이것이 문제가되는 또 다른 경우는 문자열이 일부 함수의 반환 값 인 경우입니다. string은 참조 유형이며 기술적으로 값이 null이고 비어있는 값을 가질 수 있으므로 함수는 기술적으로 null 또는 비어있는 값을 리턴 할 수 있습니다 (그렇게하는 것을 막을 방법은 없습니다). 이제 "값 없음"이라는 두 가지 개념, 즉 빈 문자열과 null이 있으므로이 함수를 사용하는 모든 코드는 2 개의 검사를 수행해야합니다. 하나는 비어 있고 다른 하나는 널입니다.

간단히 말해, 단일 상태에 대해 항상 하나의 표현 만 갖는 것이 좋습니다. 빈 및 널에 대한보다 자세한 내용은 아래 링크를 참조하십시오.

/software/32578/sql-empty-string-vs-null-value

사용자 입력을 처리 할 때 NULL 대 비어 있음


2
텍스트 상자에서이 차이를 정확히 어떻게 알 수 있습니까? 사용자가 필드에 값을 입력하는 것을 잊었습니까? 아니면 의도적으로 값을 비워 두셨습니까? 프로그래밍 언어의 널 (null)은 특정 의미를 갖습니다. 할당되지 않은. 값이 없으므로 데이터베이스 null과 동일하지 않습니다.
Andy

1
텍스트 상자와 함께 사용할 때 큰 차이가 없습니다. 어느 쪽이든 문자열에 값이 없음을 나타내는 하나의 표기법이 가장 중요합니다. 하나를 선택해야한다면 null을 선택합니다.
Nerrve

Delphi에서 string은 값 유형이므로 null 일 수 없습니다. 이 점에서 인생이 훨씬 쉬워집니다. 매우 성가신 문자열을 참조 유형으로 만듭니다.
Fabricio Araujo

1
.net 이전의 COM (Common Object Model)에서 문자열 유형은 문자열 데이터에 대한 포인터를 보유하거나 null빈 문자열을 나타냅니다. .net이 비슷한 의미론을 구현할 수있는 방법은 여러 가지가 있는데, 특히 String고유 한 유형으로 만드는 여러 가지 특성이있는 경우 (예 : 두 배열 유형이 유일한 유형 인 경우) 크기가 일정하지 않습니다].
supercat

7

근본적인 이유 / 문제는 CLS 사양의 디자이너 (언어가 .net과 상호 작용하는 방법을 정의)가 클래스 멤버가 callvirt호출자가 널 참조 검사; 또한 "정상적인"복싱에 적용되지 않는 구조를 정의하는 의미도 제공하지 않았습니다.

CLS 사양에 이러한 수단이 정의되어 있으면 .net이 COM (Common Object Model)에 의해 설정된 리드를 지속적으로 따르는 것이 가능합니다. 마찬가지로 기본값을 정의하기 위해 값 의미론을 가져야하는 사용자 정의 불변 클래스 유형. 기본적으로, 어떤의 각 구성원이 될 것 일어날 것 String예를 들면 것은, Length같은과 같이 작성한다 [InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} }. 이 접근법은 가치처럼 행동 해야하는 것들에 대해 매우 훌륭한 의미를 제공했지만 구현 문제 때문에 힙에 저장해야합니다. 이 접근 방식의 가장 큰 어려움은 이러한 유형 간의 변환 의미론이 Object약간 어둡습니다.

다른 접근 방식은 상속하지 않고 Object대신 사용자 정의 boxing 및 unboxing 작업을 수행 하는 특수 구조 유형의 정의를 허용하는 것이 었습니다 (다른 클래스 유형으로 /에서 변환). 이러한 접근 방식에서는 NullableString문자열처럼 작동 하는 클래스 유형 과 String단일 개인 필드 Value유형을 보유하는 사용자 정의 상자 구조 유형 이 있습니다 String. 변환 시도 중StringNullableString또는 Object반환 Valuenull이 아닌 경우, 또는 String.Empty경우는 null입니다. 로 캐스팅하려고 String하면 NullableString인스턴스에 대한 null이 아닌 참조 는 참조를 Value저장합니다 (길이가 0 인 경우 null을 저장함). 다른 참조를 캐스팅하면 예외가 발생합니다.

문자열을 힙에 저장해야하지만 널이 아닌 기본값을 가진 값 유형처럼 작동 하지 않아야 하는 이유는 개념적으로 없습니다 . 참조를 보유한 "정상"구조로 저장하면 "문자열"유형으로 사용하는 코드에서 효율적일 수 있지만 "객체"로 캐스팅 할 때 간접적이고 비효율적 인 계층이 추가됩니다. 이 늦은 날짜에 위의 기능 중 하나를 추가하는 .net을 예측하지는 않지만 미래 프레임 워크의 디자이너는이 기능을 포함하는 것을 고려할 수 있습니다.


1
SQL에서 많이 일하고 NULL과 0 길이를 구별하지 않는 Oracle의 두통을 처리 한 사람으로 말하면 .NET 매우 기쁩니다 . "빈"은 값이고 "널"은 아닙니다.

@JonofAllTrades : 동의하지 않습니다. 응용 프로그램 코드에서 db 코드 처리를 제외하고는 문자열을 클래스로 취급한다는 의미는 없습니다. 가치 유형과 기본 유형입니다. 슈퍼 캣 : +1 당신에게
Fabricio Araujo

1
데이터베이스 코드는 큰 "제외"입니다. 언제 까지나 있기 때문에 일부 데이터베이스 같은 당신이 "현재 / 알려져, 빈 문자열"과 "존재하지 / 알 / 적용 할"을 구별해야하는 문제 영역은 다음 언어 요구를 지원합니다. 물론 이제 .NET은 Nullable<>, 문자열을 값 유형으로 다시 구현할 수 있습니다. 나는 그러한 선택의 비용과 이점에 대해 말할 수 없습니다.

3
@JonofAllTrades : 숫자를 다루는 코드는 기본값 0을 "정의되지 않음"과 구별하는 대역 외 수단을 가져야합니다. 따라서 문자열 및 숫자와 함께 작동하는 nullable 처리 코드는 nullable 문자열에 대해 한 가지 방법과 nullable 숫자에 대해 다른 방법을 사용해야합니다. 널 입력 가능 클래스 유형 string이 이전보다 더 효율적 이더라도 Nullable<string>"더 효율적인"방법을 사용하는 것은 모든 널 입력 가능 데이터 데이터베이스 값에 대해 동일한 접근 방식을 사용할 수있는 것보다 훨씬 부담이됩니다.
supercat

5

문자열 변수는 인스턴스 가 아니라 참조 이기 때문 입니다.

기본적으로 비어 있음으로 초기화하는 것이 가능했지만 보드 전체에 많은 불일치가 발생했습니다.


3
특별한 이유 string는 없을 것입니다 참조 유형입니다. 확실히, 문자열을 구성하는 실제 문자는 반드시 힙에 저장되어야하지만 문자열이 CLR에 이미있는 전용 지원의 양을 감안할 때 System.String값이 Value유형의 단일 개인 필드 HeapString. 해당 필드는 참조 유형이며 기본값은로 설정 null되지만 필드가 null String인 구조체 Value는 빈 문자열로 동작합니다. 이 접근법의 유일한 단점은 다음과 같습니다.
supercat

1
... 런타임에 특수 사례 코드가없는 경우 Stringto 를 캐스팅하면 단순히에 대한 참조를 복사하는 대신 힙에 Object박스형 String인스턴스 가 생성 HeapString됩니다.
supercat

1
@ supercat-아무도 문자열이 값 유형이어야한다고 말하지 않습니다.
Henk Holterman

1
나 외에는 아무도 없어 문자열을 "특별한"값 유형 (개인 참조 유형 필드 포함)으로 설정하면 메소드 / 속성 .Length등에 대한 추가 null 검사를 제외하고 인스턴스를 보유하는 경우를 제외하고는 대부분의 처리가 현재처럼 효율적으로 수행 될 수 있습니다 . null 참조는 참조를 역 참조하지 않고 빈 문자열에 적합하게 동작합니다. string만약 default(string)빈 문자열이되고 싶다면 프레임 워크가 더 나은지 나쁜지 ...
supercat

1
... string참조 유형 필드에서 값 유형 래퍼를 갖는 것은 .net의 다른 부분에 대한 변경이 거의 필요하지 않은 접근 방식입니다. (실제로 하나의 상자 항목 StringObject만들기 위해 변환을 수락하려는 경우 , 하나는 단순히 노출되지 않은 String타입의 필드를 가진 일반적인 구조체 일 수 있다 Char[]]. 나는 HeapString타입을 갖는 것이 더 좋을 것이라고 생각 하지만 어떤면에서는 a를 보유한 값 타입 문자열 Char[]이 더 간단합니다.
supercat

5

C #의 디자이너가 null을 문자열의 기본값으로 사용하기로 선택한 이유는 무엇입니까?

문자열은 참조 유형 이므로 참조 유형은 기본값입니다.null . 참조 유형의 변수는 실제 데이터에 대한 참조를 저장합니다.

default이 경우 키워드를 사용하십시오 .

string str = default(string); 

strA는 string, 그래서 그것은이다 참조 형은 , 그래서 기본 값입니다 null.

int str = (default)(int);

strint그것이하므로, 값 유형 기본값은 그래서 zero.


4

의 기본값 string이 빈 문자열이면 테스트 할 필요가 없습니다.

잘못된! 기본값을 변경해도 참조 유형이라는 사실은 변경되지 않으며 누군가가 여전히 참조를 명시 적으로로 설정할 수 있습니다 null.

또한 Nullable<String>의미가 있습니다.

사실입니다. null참조 유형을 허용하지 않고 Nullable<TheRefType>해당 기능을 요구하는 것이 더 합리적 입니다.

그렇다면 C # 디자이너 null가 문자열의 기본값 으로 사용하기 로 선택한 이유는 무엇입니까?

다른 참조 유형과의 일관성 이제 왜 null참조 유형을 허용 합니까? 아마도 이것이 C와 같은 느낌을 갖도록하기도하는데, 이것은 또한 언어를 제공하는 언어로 의심스러운 디자인 결정 일지라도 말입니다 Nullable.


3
Nullable이 .NET 2.0 Framework에서만 도입 되었기 때문에 그 전에는 사용할 수 없었습니까?
jcolebrand

3
나중에 참조 유형에서 누군가 초기화 된 값을 null로 설정할 수 있다고 지적한 Dan Burton에게 감사합니다. 이것을 통해 생각하면 그 질문의 원래 의도가 쓸모가 없다는 것을 알 수 있습니다.
Marcel

4

??문자열 변수를 지정할 때 연산자를 사용 하면 도움이 될 수 있습니다.

string str = SomeMethodThatReturnsaString() ?? "";
// if SomeMethodThatReturnsaString() returns a null value, "" is assigned to str.

2

문자열은 불변의 객체로, 값이 주어지면 이전 값은 메모리에서 지워지지 않지만 이전 위치에는 남아 있으며 새 값은 새로운 위치에 놓임을 의미합니다. 따라서 기본값 String aString.Empty이면String.Empty 첫 번째 값이 주어지면 블록을 메모리에 .

비록 사소한 것처럼 보이지만, 기본값이 기본값 인 큰 문자열 배열을 초기화 할 때 문제가 될 수 있습니다 String.Empty. 물론 StringBuilder이것이 문제가 될 경우 언제든지 변경 가능한 클래스를 사용할 수 있습니다.


"최초 초기화"에 대해 언급 해 주셔서 감사합니다.
Marcel

3
큰 배열을 초기화 할 때 어떻게 문제가됩니까? 앞에서 말했듯이 Strings는 불변이므로 배열의 모든 요소는 단순히 같은 포인터 String.Empty입니다. 내가 착각 했니?
Dan Burton

2
모든 유형 의 기본값 은 모든 비트가 0으로 설정됩니다. 기본값 string이 빈 문자열이 되는 유일한 방법은 모든 문자열을 0으로 설정하여 빈 문자열을 나타내는 것입니다. 이 작업을 수행하는 방법에는 여러 가지가 있지만에 대한 참조를 초기화하는 것은 아닙니다 String.Empty.
supercat

다른 답변들도이 점에 대해 논의했습니다. 나는 사람들이 그것이 뭔가를했다하더라도 특별한 경우로 String 클래스를 치료하고 모든 비트 제로 초기화 등 이외의를 제공하기 위해 이해하지 않을 것이라고 결론을 내렸다 생각 String.Empty이나 "".
djv

@DanV : string스토리지 위치의 초기화 동작을 변경하려면 유형 필드가있는 모든 구조체 또는 클래스의 초기화 동작도 변경해야합니다 string. 이것은 .net 디자인에서 상당히 큰 변화를 나타낼 것입니다. 현재 .net 형식을 생각하지 않고도 모든 유형을 0으로 초기화 할 것을 기대합니다. 총 크기 만 절약하십시오.
supercat


0

어쩌면 string키워드는 다른 값 유형 선언 과 똑같이 보이기 때문에 혼란 스럽지만 실제로이 질문에System.String 설명 된 것처럼 별칭 입니다. 또한 Visual Studio의 짙은 파란색과 소문자 첫 글자가 잘못 생각할 수 있습니다 .
struct


3
object키워드도 마찬가지 입니까? 인정하지만, 그것보다 훨씬 덜 사용됩니다 string.

2
으로는 int별칭입니다 System.Int32. 너의 요점이 뭐야? :)
Thorarin

그들은 모두 별칭을,하지만 : @Thorari는 @delnan System.Int32A는 Struct동안 따라서 디폴트 값을 갖는 System.StringA는 Class디폴트 값에 대한 포인터를 가지고 null. 시각적으로 동일한 글꼴 / 색상으로 표시됩니다. 지식이 없으면 동일한 방식으로 작동한다고 생각할 수 있습니다 (= 기본값 사용). 내 대답은 작성되었다 en.wikipedia.org/wiki/Cognitive_psychology :-) 뒤에인지 심리학 아이디어
알레산드로 다 Rugna을

나는 채널 9 인터뷰에서 Anders Hejlsberg가 말한 것을 꽤 확신합니다. 힙과 스택의 차이점을 알고 있지만 C #의 아이디어는 캐주얼 프로그래머가 필요하지 않다는 것입니다.
토마스 코 엘레

0

널 입력 가능 유형은 2.0까지 제공되지 않았습니다.

언어의 시작 부분에 널 입력 가능 유형이 있다면 문자열은 널 입력 가능하지 않고 문자열입니까? 무효가되었을 것입니다. 그러나 이전 버전과의 호환성을 위해이 작업을 수행 할 수 없었습니다.

많은 사람들이 ref-type 또는 ref-type에 대해 이야기하지만 문자열은 일반적인 클래스에서 벗어 났으며 솔루션은 가능해졌습니다.

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