널 문자열을 연결하는 것이 유효하지만“null.ToString ()”을 호출하지 않는 이유는 무엇입니까?


126

이것은 유효한 C # 코드입니다

var bob = "abc" + null + null + null + "123";  // abc123

유효한 C # 코드가 아닙니다.

var wtf = null.ToString(); // compiler error

첫 번째 진술이 왜 유효합니까?


97
나는 당신 null.ToString()의 이름이 주어진다는 것이 특별하다는 것을 알게되었습니다 wtf. 왜 놀랍습니까? 처음부터 호출 할 것이 없으면 인스턴스 메소드를 호출 할 수 없습니다.
BoltClock

11
@BoltClock : 물론 null 인스턴스에서 인스턴스 메소드를 호출하는 것이 가능합니다. C #에서는 가능하지 않지만 CLR에서는 매우 유효합니다 :)
leppie

1
String.Concat에 대한 답변은 거의 정확합니다. 실제로 문제의 특정 예는 상수 접기 중 하나이며 첫 번째 줄의 null은 컴파일러에 의해 제거됩니다. 즉, 더 이상 존재하지 않기 때문에 런타임에 평가되지 않습니다. 컴파일러가 지 웠습니다. 자세한 내용을 보려면 여기에 문자열을 연결하고 지속적으로 접는 규칙에 대한 약간의 독백을 썼습니다 : stackoverflow.com/questions/9132338/… .
Chris Shain

77
문자열에는 아무것도 추가 할 수 없지만 문자열을 아무 것도 만들 수 없습니다.
RGB

두 번째 진술이 유효하지 않습니까? class null_extension { String ToString( Object this arg ) { return ToString(arg); } }
ctrl-alt-delor

답변:


163

첫 번째 이유 :

에서 MSDN :

문자열 연결 작업에서 C # 컴파일러는 null 문자열을 빈 문자열과 동일하게 취급하지만 원래 null 문자열의 값은 변환하지 않습니다.

+ 이항 연산자 에 대한 추가 정보 :

이항 + 연산자는 하나 또는 두 피연산자가 모두 문자열 유형 인 경우 문자열 연결을 수행합니다.

문자열 연결 피연산자가 널이면 빈 문자열이 대체됩니다. 그렇지 않으면, 문자열이 아닌 인수는 ToString유형 객체에서 상속 된 가상 메소드 를 호출하여 문자열 표현으로 변환됩니다 .

ToString반환 null하면 빈 문자열이 대체됩니다.

두 번째 오류의 원인은 다음과 같습니다.

null (C # 참조) -null 키워드는 개체를 참조하지 않는 null 참조를 나타내는 리터럴입니다. 참조 유형 변수의 기본값은 null입니다.


1
그렇다면 이것이 효과가 있을까요? var wtf = ((String)null).ToString();최근에 null을 캐스팅 할 수있는 Java에서 일하고 있습니다 .C #으로 작업 한 지 오래되었습니다.
Jochem

14
@ Jochem : 당신은 여전히 ​​null 객체에서 메소드를 호출하려고 시도하고 있습니다. 그것을 null.ToString()vs 로 생각하십시오 ToString(null).
Svish

@Svish 네, 이제 다시 생각합니다. 이것은 null 객체이므로 맞습니다. 작동하지 않습니다. Java에는 없습니다 : null 포인터 예외. 신경 쓰지 마. 귀하의 회신에 대한 Tnx! [편집 : Java : NullPointerException에서 테스트했습니다. 캐스트를 사용하면 캐스트가 없으면 컴파일되지 않는다는 차이점이 있습니다].
Jochem

일반적으로 null 키워드를 의도적으로 연결하거나 ToString 할 이유가 없습니다. 빈 문자열이 필요한 경우 string.Empty를 사용하십시오. 문자열 변수가 null인지 확인 해야하는 경우 (myString == null) 또는 string.IsNullOrEmpty (myString)을 사용할 수 있습니다. 또는 null 문자열 변수를 string.Empty로 변환하려면 myNewString = (myString == null ?? string.Empty)
csauve

@Jochem 캐스팅하면 컴파일되지만 실제로 런타임에는 NullReferenceException이 발생합니다.
jeroenh

108

+C # 의 연산자는 내부적으로로 변환 되기 때문에 String.Concat정적 메서드입니다. 그리고이 방법 null은 빈 문자열처럼 취급 됩니다. String.ConcatReflector 의 소스를 보면 다음과 같이 표시됩니다.

// while looping through the parameters
strArray[i] = (str == null) ? Empty : str;
// then concatenate that string array

(MSDN에서도 언급 함 : http://msdn.microsoft.com/en-us/library/k9c94ey1.aspx )

반면에 ToString()인스턴스 메소드는 호출 할 수 없습니다 null(어떤 유형을 사용해야 null합니까?).


2
String 클래스에는 + 연산자가 없습니다. 그래서 String.Concat으로 변환하는 컴파일러입니까?
superlogical

@superlogical 예, 컴파일러가하는 일입니다. 실제로 +C #의 문자열 연산자는 구문 설탕입니다 String.Concat.
Botz3000

추가 : 컴파일러는 가장 적합한 Concat의 과부하를 자동으로 선택합니다. 사용 가능한 과부하는 1, 2 또는 3 개의 객체 매개 변수, 4 개의 객체 매개 변수 + __arglistparams객체 배열 버전입니다.
Wormbo

어떤 문자열 배열이 수정되고 있습니까? Concat널이 아닌 문자열의 배열이 주어 졌을 때에도 항상 널이 아닌 문자열의 새 배열을 작성 합니까 ? 아니면 다른 일이 있습니까?
supercat

@supercat 수정 된 배열은 새 배열이며 개인 도우미 메서드로 전달됩니다. 리플렉터를 사용하여 코드를 직접 볼 수 있습니다.
Botz3000

29

번째 샘플 은 다음과 같이 번역됩니다.

var bob = String.Concat("abc123", null, null, null, "abs123");

Concat메소드는 입력을 확인하고 null을 빈 문자열로 변환합니다.

번째 샘플 은 다음과 같이 번역됩니다.

var wtf = ((object)null).ToString();

따라서 null여기에 참조 예외가 생성됩니다


실제로, AccessViolationException던져 질 것이다 :)
leppie

방금 :) ((object)null).ToString()=> AccessViolation ((object)1 as string).ToString()=>NullReference
leppie

1
NullReferenceException이 있습니다. DN 4.0
Viacheslav Smityukh가

흥미 롭군 내가 넣을 때 ((object)null).ToString()돌며 try/catch내가 얻을 NullReferenceException너무.
leppie

3
@Ieppie를 제외하고는 AccessViolation도 발생하지 않습니다 (왜 그럴까요?)-NullReferenceException here.
jeroenh

11

코드의 첫 번째 부분은에서와 같이 취급됩니다 String.Concat.

이것은 문자열을 추가 할 때 C # 컴파일러가 호출하는 것입니다. " abc" + null로 번역됩니다 String.Concat("abc", null).

내부적으로이 방법은 null로 대체 됩니다 String.Empty. 따라서 코드의 첫 번째 부분에서 예외가 발생하지 않습니다. 그것은 같다

var bob = "abc" + string.Empty + string.Empty + string.Empty + "123";  //abc123

그리고 'null'이 객체가 아니기 때문에 코드의 두 번째 부분에서 예외가 발생 합니다 .null 키워드는 객체를 참조하지 않는 null 참조를 나타내는 리터럴입니다 . 참조 유형 변수의 기본값은 null입니다.

그리고 ' ToString()'는 객체의 인스턴스에 의해 호출 될 수 있지만 리터럴은 아닙니다.


3
String.Empty와 같지 않습니다 null. 와 같은 일부 메소드에서 동일한 방식으로 처리되었습니다 String.Concat. 예를 들어, 문자열 변수를로 설정하면 nullC #은 String.Empty메서드를 호출하려고 할 때이를 대체하지 않습니다 .
Botz3000

3
거의. nullString.EmptyC #에서 다루지 않고 String.Concat문자열에서 추가 할 때 C # 컴파일러가 호출하는 방식으로 처리됩니다. "abc" + null로 번역됩니다 String.Concat("abc", null), 내부적으로, 그 방법을 대체 nullString.Empty. 두 번째 부분은 완전히 정확합니다.
Botz3000

9

.net 이전의 COM 프레임 워크에서는 문자열을 수신 한 루틴이 완료되면이를 해제 할 필요가있었습니다. 빈 문자열이 루틴으로 들어오고 나가는 것이 매우 일반적이기 때문에 널 포인터를 "해제"하려는 시도는 합법적 인 수행 작업으로 정의 되었기 때문에 Microsoft는 널 문자열 포인터가 빈 문자열을 나타내도록 결정했습니다.

COM과의 일부 호환성을 위해 .net의 많은 루틴은 null 개체를 빈 문자열로 유효한 표현으로 해석합니다. .net 및 해당 언어의 약간의 변경 (대부분 인스턴스 멤버가 "가상으로 호출하지 않음"을 표시하도록 허용 함)을 통해 Microsoft null는 선언 된 유형 String의 개체를 빈 문자열처럼 동작하도록 만들 수있었습니다 . 만약 마이크로 소프트가 그렇게했다면, Nullable<T>작업을 다소 다르게 Nullable<String>해야했고 (어쨌든 IMHO가해야 할 일 을 허용하기 위해 ) 그리고 / 또는 NullableString대체로 상호 교환이 가능 String하지만 null유효한 빈 문자열로.

null그대로 a는 합법적 인 빈 문자열로 간주되고 그렇지 않은 컨텍스트 도 있습니다. 매우 유용한 상황은 아니지만 프로그래머가 알아야 할 상황입니다. 일반적으로 is 인 stringValue.someMember경우 양식의 표현식 이 실패 하지만 매개 변수로 문자열을 허용하는 대부분의 프레임 워크 메소드 및 연산자 는 빈 문자열로 간주 됩니다.stringValuenullnull


5

'+'중위 연산자입니다. 다른 연산자와 마찬가지로 실제로 메서드를 호출합니다. 비-고정 버전을 상상할 수 있습니다"wow".Plus(null) == "wow"

구현자는 이와 같은 것을 결정했습니다 ...

class String
{
  ...
  String Plus(ending)
  {
     if(ending == null) return this;
     ...
  }
} 

그래서 .. 당신의 모범은

var bob = "abc".Plus(null).Plus(null).Plus(null).Plus("123");  // abc123

이것은 같은

var bob = "abc".Plus("123");  // abc123

어느 시점에서도 null은 문자열이되지 않습니다. 그렇게 null.ToString()다르지 않습니다 null.VoteMyAnswer(). ;)


실제로는 더 비슷 var bob = Plus(Plus(Plus(Plus("abc",null),null),null),"123");합니다. 연산자 오버로드는 마음에서 정적 방법은 다음과 같습니다 msdn.microsoft.com/en-us/library/s53ehcz3(v=VS.71).aspx는 그들이 아니었다면, var bob = null + "abc";또는 특히 string bob = null + null;유효하지 않을 것입니다.
벤 모셔

네 말이 맞아, 내가 그렇게 설명하면 너무 혼란 스러울 것 같아서
Nigel Thorne

3

객체를 참조하지 않는 리터럴 이기 때문에 추측 합니다. ToString()필요합니다 object.


3

누군가이 토론 스레드 에서 아무 것도 문자열을 만들 수 없다고 말했습니다 . (내 생각에는 좋은 문구입니다). 그러나 예- 다음 예제와 같이 :-) 할 수 있습니다 .

var x = null + (string)null;     
var wtf = x.ToString();

잘 작동하고 전혀 예외를 throw하지 않습니다. 유일한 차이점은 널 중 하나를 문자열로 캐스트해야한다는 것입니다. (문자열) 캐스트 를 제거 하면 예제는 여전히 컴파일되지만 런타임 예외가 발생합니다. "연산자 '+'는 피연산자에 대해 모호합니다. '<null>'및 '<null>' "을 입력하십시오.

NB 위의 코드 예제에서 x의 값은 예상대로 null이 아니며 피연산자 중 하나를 문자열로 캐스팅 한 후에 실제로는 빈 문자열입니다.


또 다른 흥미로운 사실은 C # / .NET 에서null 다른 데이터 형식을 고려할 때 처리 방식 이 항상 동일하지는 않다는 것 입니다. 예를 들면 다음과 같습니다.

int? x = 1;  //  string x = "1";
x = x + null + null;
Console.WriteLine((x==null) ? "<null>" : x.ToString());

코드 스 니펫 의 첫 번째 줄 에 대해 : value를 포함 x하는 nullable 정수 변수 인 경우 결과를 다시 얻습니다 . 이 값 (같은 주석 참조) 문자열 인 경우 에, 당신은 얻고 다시보다는 .int?1<null>"1""1"<null>

NB 또한 흥미로운 점 : var x = 1;첫 번째 줄에 사용 하는 경우 런타임 오류가 발생합니다. 왜? 할당은 변수 x를 datatype으로 변환하므로 intnullable은 아닙니다. 컴파일러는 int?여기서 가정하지 않으므로 null추가 된 두 번째 줄에서 실패합니다 .


2

null문자열에 추가 하는 것은 단순히 무시됩니다. null(두 번째 예에서)는 객체의 인스턴스가 아니므로 ToString()메소드 조차 없습니다 . 그냥 리터럴입니다.


1

문자열을 연결하는 시점 string.Empty과 시간 사이에 차이가 없기 때문입니다 null. null도 전달할 수 있습니다 string.Format. 그러나 on 메소드를 호출하려고하면 null항상 결과적 NullReferenceException으로 컴파일러 오류가 발생합니다.
어떤 이유로 든 정말로하려는 경우 확장 메서드를 작성하여 확인한 null다음 반환합니다 string.Empty. 그러나 그러한 확장은 절대적으로 필요할 때만 사용해야합니다 (제 의견으로는).


0

일반적으로 : 사양에 따라 null을 매개 변수로 허용하는 것이 유효하거나 유효하지 않을 수 있지만 null에서 메서드를 호출하는 것은 항상 유효하지 않습니다.


그것이 문자열의 경우 + 연산자의 피연산자가 null이 될 수있는 이유와 다른 주제입니다. 이것은 프로그래머가 삶을 더 쉽게 만들거나 프로그래머가 null을 처리 할 수 ​​없다고 생각하는 VB 일입니다. 이 사양에 완전히 동의하지 않습니다. '알 수 없음'+ '모든 것'은 여전히 ​​'알 수 없음'이어야합니다 ...

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