null + true는 어떻게 문자열입니까?


112

true문자열 유형이 아니기 때문에 null + true문자열은 어떻습니까?

string s = true;  //Cannot implicitly convert type 'bool' to 'string'   
bool b = null + true; //Cannot implicitly convert type 'string' to 'bool'

그 이유는 무엇입니까?


27
말이 안되는 일을하고 컴파일러가 생성하는 메시지가 마음에 들지 않습니까? 이것은 유효하지 않은 C # 코드입니다 ... 정확히 무엇을 원했습니까?
Hogan

19
@Hogan :이 코드는 순수한 호기심으로 질문 할 수있을만큼 무작위적이고 학술적으로 보입니다. 내 생각 엔 ...
BoltClock

8
null + true는 JavaScript에서 1을 반환합니다.
Fatih Acet

답변:


147

이상하게 보일지 모르지만 단순히 C # 언어 사양의 규칙을 따르는 것입니다.

섹션 7.3.4에서 :

x op y 형식의 연산은 다음과 같이 처리됩니다. 여기서 op는 오버로드 가능한 이항 연산자, x는 X 유형의 표현식, y는 Y 유형의 표현식입니다.

  • 연산 연산자 op (x, y)에 대해 X 및 Y에 의해 제공되는 후보 사용자 정의 연산자 집합이 결정됩니다. 집합은 X가 제공하는 후보 연산자와 Y가 제공하는 후보 연산자의 합집합으로 구성되며, 각각 §7.3.5의 규칙을 사용하여 결정됩니다. X와 Y가 동일한 유형이거나 X와 Y가 공통 기본 유형에서 파생 된 경우 공유 후보 연산자는 결합 된 집합에서 한 번만 발생합니다.
  • 후보 사용자 정의 연산자 집합이 비어 있지 않으면이 연산자가 작업에 대한 후보 연산자 집합이됩니다. 그렇지 않으면 해제 된 형식을 포함하여 미리 정의 된 이항 연산자 op 구현이 작업에 대한 후보 연산자 집합이됩니다. 지정된 연산자의 미리 정의 된 구현은 연산자 설명에 지정되어 있습니다 (§7.8 ~ §7.12).
  • §7.5.3의 오버로드 해결 규칙은 인수 목록 (x, y)과 관련하여 최상의 연산자를 선택하기 위해 후보 연산자 집합에 적용되며이 연산자는 오버로드 해결 프로세스의 결과가됩니다. 과부하 해결이 단일 최상의 연산자를 선택하지 못하면 바인딩 시간 오류가 발생합니다.

자, 이것을 차례로 살펴 보겠습니다.

X는 여기서 null 유형입니다. 그런 식으로 생각하고 싶다면 유형이 아닙니다. 후보자를 제공하지 않습니다. Y는bool 사용자 정의 +연산자를 제공하지 않는 입니다. 따라서 첫 번째 단계에서는 사용자 정의 연산자를 찾지 않습니다.

그런 다음 컴파일러는 두 번째 글 머리 기호로 이동하여 미리 정의 된 이항 연산자 + 구현 및 해제 된 형식을 살펴 봅니다. 사양의 섹션 7.8.4에 나열되어 있습니다.

사전 정의 된 연산자를 살펴보면 적용 가능한 유일한 연산자 는 string operator +(string x, object y). 따라서 후보 세트에는 단일 항목이 있습니다. 따라서 최종 글 머리 기호가 매우 간단 해집니다. 과부하 해결은 해당 연산자를 선택하여 전체 표현식 유형을string .

한 가지 흥미로운 점은 언급되지 않은 유형에서 사용할 수있는 다른 사용자 정의 연산자가 있더라도 이것이 발생한다는 것입니다. 예를 들면 :

// Foo defined Foo operator+(Foo foo, bool b)
Foo f = null;
Foo g = f + true;

괜찮지 만 컴파일러가를 찾는 것을 모르기 때문에 null 리터럴에는 사용되지 않습니다 Foo. string사양에 명시 적으로 나열된 사전 정의 된 연산자이기 때문에 고려하는 것만 알고 있습니다. (사실, 이것은 문자열 유형으로 정의 된 연산자 가 아닙니다 ... 1 ) 이것은 컴파일에 실패한다는 것을 의미합니다 :

// Error: Cannot implicitly convert type 'string' to 'Foo'
Foo f = null + true;

다른 두 번째 피연산자 유형은 물론 다른 연산자를 사용합니다.

var x = null + 0; // x is Nullable<int>
var y = null + 0L; // y is Nullable<long>
var z = null + DayOfWeek.Sunday; // z is Nullable<DayOfWeek>

1 문자열 + 연산자가 없는지 궁금 할 것 입니다. 합리적인 질문이고 답을 추측 하고 있지만 다음 표현을 고려하십시오.

string x = a + b + c + d;

경우 stringC # 컴파일러에는 특수 케이스가 없었다,이 효율적으로 끝낼 것입니다 :

string tmp0 = (a + b);
string tmp1 = tmp0 + c;
string x = tmp1 + d;

두 개의 불필요한 중간 문자열이 만들어졌습니다. 그러나 컴파일러 내에 특별한 지원이 있기 때문에 실제로 위의 내용을 다음과 같이 컴파일 할 수 있습니다.

string x = string.Concat(a, b, c, d);

정확히 올바른 길이의 단일 문자열을 생성하여 모든 데이터를 정확히 한 번 복사 할 수 있습니다. 좋은.


'전체적인 표현 유형의 문자열 제공'나는 단순히 동의하지 않습니다. 이 오류에서 오는 true으로 변환되지 않는 string. 표현식이 유효하면 유형은 string이지만이 경우 문자열로 변환하지 못하면 전체 표현식이 오류가되므로 유형이 없습니다.
leppie

6
@leppie : 표현식 유효하고 유형이 문자열입니다. "var x = null + true;"를 시도하십시오. -컴파일되고 x유형 string입니다. 여기에 사용 된 string operator+(string, object)서명은 bool으로 변환 object되지 않고 (괜찮습니다) 로 변환 됩니다 string.
Jon Skeet

감사합니다 Jon, 이제 이해하십시오. 사양 버전 2의 BTW 섹션 14.7.4.
leppie

@leppie : ECMA 번호에 포함되어 있습니까? 그것은 항상 매우 달랐습니다. :(
Jon Skeet

47
20 분 안에 그는이 모든 것을 20 분 만에 썼습니다. 그는 책을 써야 해요 ... 아 잠깐만 요.
Epaga

44

그 이유는 일단 소개 +하면 C # 연산자 바인딩 규칙이 적용 되기 때문 입니다. +사용 가능한 연산자 집합을 고려 하고 최상의 과부하를 선택합니다. 그 연산자 중 하나는 다음과 같습니다.

string operator +(string x, object y)

이 오버로드는 식의 인수 유형과 호환됩니다 null + true. 따라서 연산자로 선택되고 본질적으로 ((string)null) + true값을 평가하는 것으로 평가됩니다 "True".

C # 언어 사양의 섹션 7.7.4에는이 해결 방법에 대한 세부 정보가 포함되어 있습니다.


1
생성 된 IL이 곧바로 Concat을 호출하는 것으로 나타났습니다. 나는 거기에 + 연산자 함수에 대한 호출을 볼 것이라고 생각했습니다. 단순히 인라인 최적화일까요?
quentin-starin

1
내가 생각 @qstarin 이유가되지 않는 것입니다 정말operator+ 에 대한 string. 대신 컴파일러의 마음 속에 만 존재하며 단순히 다음을위한 호출로 변환합니다.string.Concat
JaredPar

1
@qstarin @JaredPar : 내 대답에서 좀 더 자세히 살펴 보았습니다.
Jon Skeet

11

컴파일러는 먼저 null 인수를받을 수있는 operator + ()를 찾습니다. 표준 값 유형 중 어느 것도 규정하지 않습니다. null은 유효한 값이 아닙니다. 유일한 일치는 System.String.operator + ()이며 모호성이 없습니다.

해당 연산자의 두 번째 인수도 문자열입니다. 그것은 kapooey로 가고, bool을 문자열로 암시 적으로 변환 할 수 없습니다.


10

흥미롭게도 Reflector를 사용하여 생성 된 내용을 검사하면 다음 코드가 있습니다.

string b = null + true;
Console.WriteLine(b);

컴파일러에 의해 다음과 같이 변환됩니다.

Console.WriteLine(true);

이 "최적화"뒤에있는 이유는 내가 말해야 할 약간 이상하고 내가 기대하는 연산자 선택과 일치하지 않습니다.

또한 다음 코드 :

var b = null + true; 
var sb = new StringBuilder(b);

로 변형됩니다

string b = true; 
StringBuilder sb = new StringBuilder(b);

string b = true;실제로 컴파일러에서 허용되지 않는 곳 입니다.


8

nullnull 문자열로 캐스트되고 bool에서 문자열로의 암시 적 변환기가 있으므로는 문자열 true로 캐스트되고 +연산자가 적용됩니다. 다음과 같습니다. string str = ""+ true.ToString ();

Ildasm으로 확인하면 :

string str = null + true;

다음과 같습니다.

.locals init ([0] string str)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  box        [mscorlib]System.Boolean
  IL_0007:  call       string [mscorlib]System.String::Concat(object)
  IL_000c:  stloc.0

5
var b = (null + DateTime.Now); // String
var b = (null + 1);            // System.Nullable<Int32> | same with System.Single, System.Double, System.Decimal, System.TimeSpan etc
var b = (null + new Object()); // String | same with any ref type

미친?? 아니요, 그 뒤에 이유가있을 것입니다.

누군가 전화 Eric Lippert...


5

그 이유는 편리하기 때문입니다 (문자열 연결은 일반적인 작업입니다).

BoltClock이 말했듯이 '+'연산자는 숫자 유형, 문자열에 정의되며 자체 유형에 대해서도 정의 할 수 있습니다 (연산자 오버로딩).

인수 유형에 오버로드 된 '+'연산자가없고 숫자 유형이 아닌 경우 컴파일러는 기본적으로 문자열 연결을 사용합니다.

컴파일러는 String.Concat(...)'+'를 사용하여 연결하면에 대한 호출을 삽입하고 Concat 구현은 전달 된 각 개체에 대해 ToString을 호출합니다.

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