String.Empty와“”(빈 문자열)의 차이점은 무엇입니까?


288

.NET에서 String.Empty와 의 차이점은 무엇이며 ""상호 교환이 가능 String.Empty합니까 , 아니면 평등에 관한 기본 참조 또는 현지화 문제 가 문제가되지 않습니까?


2
진짜 문제는 아닙니다 무엇 오히려 . 왜 Microsoft가 등장 string.Empty했는지와 readonly대신에 그것을 선언 한 이유는 무엇입니까 const?
user1451111

답변:


295

버전 2.0 이전 .NET에서 ""동안 객체를 생성 string.Empty어떤 객체 생성되지 심판 하게하는, string.Empty보다 효율적으로.

버전 2.0 이상에서 .NET의 모든 항목은 ""수단이 동일한 문자열 리터럴을 참조 ""에 해당 .Empty아직도 최대한 빨리,하지만 .Length == 0.

.Length == 0가장 빠른 옵션이지만 .Empty약간 더 깨끗한 코드를 만듭니다.

자세한 내용은 .NET 사양을 참조하십시오 .


91
""은 (는) 문자열 삽입으로 인해 한 번만 객체를 만들었을 것입니다. 기본적으로 성능 트레이드 오프는 땅콩입니다. 가독성이 더 중요합니다.
Jon Skeet

12
질문이 문자열을 ""또는 문자열과 비교하는 것에 대해 아무 말도하지 않는다는 점에 흥미가 있습니다. 빈 문자열을 확인하는
데는

11
문자열 변수가 null 인 경우 예외가 발생할 수 있으므로 .Length == 0으로주의를 기울여야합니다. ""에 대해 검사하면 예외없이 false를 올바르게 반환합니다.
Jeffrey Harmon

11
@JeffreyHarmon : 또는 사용할 수 있습니다 string.IsNullOrEmpty( stringVar ).
Flynn1179

1
빈 문자열을 테스트하기 위해 잘못된 방법을 방지하려는 경우 코드 분석에서 CA1820을 활성화 할 수 있습니다. docs.microsoft.com/visualstudio/code-quality/…
sean

197

String.Empty와 ""의 차이점은 무엇이며 상호 교환 가능합니까?

string.Empty읽기 전용 필드 인 반면 ""컴파일 시간 상수입니다. 다르게 행동하는 장소는 다음과 같습니다.

C # 4.0 이상의 기본 매개 변수 값

void SomeMethod(int ID, string value = string.Empty)
// Error: Default parameter value for 'value' must be a compile-time constant
{
    //... implementation
}

switch 문의 사례 표현

string str = "";
switch(str)
{
    case string.Empty: // Error: A constant value is expected. 
        break;

    case "":
        break;

}

속성 인수

[Example(String.Empty)]
// Error: An attribute argument must be a constant expression, typeof expression 
//        or array creation expression of an attribute parameter type

21
흥미롭게도, 나는이 오래된 질문이 새로운 관련 정보를 얻지 못할 것이라고 생각했습니다. 내가 틀렸어
johnc

.NET이 기본 매개 변수를 속성에 배포한다고 믿기 때문에 C # 4.0 이상의 예제 # 1 기본 매개 변수 값 은 본질적으로 예제 # 3 속성 인수 의 복제본 이라고 생각합니다. 따라서 기본적으로 (런타임) "값"(이 경우 stickler를위한 인스턴스 핸들 )을 (컴파일 타임) 메타 데이터에 넣을 수 없습니다 .
Glenn Slayden

@GlennSlayden, 나는 당신에 동의하지 않습니다. 속성 초기화는 일반 초기화와 다릅니다. String.Empty대부분의 경우 인수를 인수로 전달할 수 있기 때문 입니다. 모든 예가 그 사실을 보여주는 것이 맞습니다. 바로 그 예가 보여주고 자하는 you simply can't put a (run-time) "value" into (compile-time) metadata것입니다.
우치하 사스케

42

이전 답변은 .NET 1.1에 적합했습니다 (링크 된 게시물 날짜 : 2003). .NET 2.0 이상에서는 본질적으로 차이가 없습니다. JIT는 어쨌든 힙에서 동일한 객체를 참조하게됩니다.

C # 사양에 따라 2.4.4.5 섹션 : http://msdn.microsoft.com/en-us/library/aa691090(VS.71).aspx

각 문자열 리터럴이 반드시 새로운 문자열 인스턴스가 될 필요는 없습니다. 문자열 같음 연산자 (7.9.7 단원)에 따라 동등한 둘 이상의 문자열 리터럴이 동일한 어셈블리에 나타날 경우 이러한 문자열 리터럴은 동일한 문자열 인스턴스를 나타냅니다.

누군가 브래드 아브람 (Brad Abram)의 게시물에서 이것을 언급했습니다.

요약하면 ""대 String.Empty의 실제 결과는 nil입니다. JIT는 결국 그것을 알아낼 것입니다.

개인적으로 JIT가 나보다 똑똑하다는 것을 알았으므로 마이크로 컴파일러 최적화로 너무 영리하지 않도록 노력하십시오. JIT는 for () 루프를 펼치고 중복 코드, 인라인 메소드 등을 더 적절하고 적절한 시점에 I 또는 C # 컴파일러가 사전에 예상했던 것보다 더 적절한 시간에 전개합니다. JIT가 그 일을하게하십시오 :)


1
작은 오타? "JIT는 어쨌든 도움말에서 동일한 객체를 참조하게됩니다." 당신은 "힙에"를 의미 했습니까?
Dana


36

String.EmptyA는 읽기 전용 동안 필드 ""A는 CONST . 이것은 String.Empty상수가 아니기 때문에 switch 문에서 사용할 수 없음을 의미합니다 .


의 출현으로 default우리가없이 가독성을 홍보 할 수있는 키워드, 정직하게하지만, 난 여전히 String.Empty로 기본보다 더 읽을 생각하지만, 느린 입력, 우발적 인 변경을 방지하고, 컴파일 타임 상수가
Enzoaeneas

18

또 다른 차이점은 String.Empty가 더 큰 CIL 코드를 생성한다는 것입니다. ""및 String.Empty를 참조하는 코드의 길이는 동일하지만 컴파일러는 String.Empty 인수에 대해 문자열 연결 (Eric Lippert의 블로그 게시물 참조)을 최적화하지 않습니다 . 다음과 같은 기능

string foo()
{
    return "foo" + "";
}
string bar()
{
    return "bar" + string.Empty;
}

이 IL을 생성

.method private hidebysig instance string foo() cil managed
{
    .maxstack 8
    L_0000: ldstr "foo"
    L_0005: ret 
}
.method private hidebysig instance string bar() cil managed
{
    .maxstack 8
    L_0000: ldstr "bar"
    L_0005: ldsfld string [mscorlib]System.String::Empty
    L_000a: call string [mscorlib]System.String::Concat(string, string)
    L_000f: ret 
}

타당하지만 누가 그런 짓을합니까? 빈 문자열을 의도적으로 무언가에 연결해야하는 이유는 무엇입니까?
Robert S.

@RobertS. 아마도 두 번째 문자열은 인라인 된 별도의 함수에 있었을 것입니다. 드물다, 난 당신을 부여합니다.
Bruno Martinez

3
삼항 연산자를 사용하는 것은 그리 드물지 않습니다."bar " + (ok ? "" : "error")
symbiont

13

위의 답변은 기술적으로 정확하지만 최상의 코드 가독성과 예외 가능성을 최소화하기 위해 실제로 사용하려는 것은 String입니다.


3
평등 비교의 관점에서, 나는 완전히 동의하지만, 문제는 또한 비교뿐만 아니라 두 개념의 차이점에 관한 것이었다
johnc

1
"예외가 발생할 가능성이 가장 적다"는 말은 "계속 될 가능성이 있지만 무언가 잘못 될 가능성이 높다"는 것을 의미합니다. 예를 들어, 구문 분석하는 명령 줄 인수가 있고 누군가가 앱을 호출 --foo=$BAR하는 경우 env var 설정을 잊어 버리고 플래그를 전혀 전달하지 않는 것의 차이점을 확인하고 싶을 것입니다. string.IsNullOrEmpty입력을 올바르게 확인하지 않았거나 이상한 일을하는 코드 냄새가 종종 있습니다. 실제로 null, 또는 Maybe / Option 유형과 같은 것을 사용할 때 빈 문자열을 허용해서는 안됩니다 .
Alastair Maw

11

내가 사용하는 경향 String.Empty보다는 "", 하나의 간단한 아직없는 명백한 이유를 : """"동일하지 않습니다, 첫 번째는 실제로 16 개 제로 폭 문자가 있습니다. 유능한 개발자는 코드에 폭이 0 인 문자를 넣지 않을 것입니다. 그러나 코드에 들어가면 유지 관리가 악몽이 될 수 있습니다.

노트:

  • 내가 사용 U + FEFF을 이 예에.

  • SO가 그 문자를 먹을지 확실하지 않지만 많은 0 너비 문자 중 하나를 사용하여 직접 시도하십시오.

  • https://codegolf.stackexchange.com/ 덕분 에이 문제에 직면했습니다.


이로 인해 더 많은 투표가 필요합니다. 이 문제는 플랫폼 간 시스템 통합을 통해 발생합니다.
EvilDr

8

String.Empty보다 사용하십시오 "".

이것은 메모리 사용량보다 속도가 빠르지 만 유용한 팁입니다. 는 ""리터럴 그래서 리터럴 역할을 할 것이다 : 제 용도에 생성되고, 다음에 참조 용으로 반환된다. ""사용 횟수에 관계없이 하나의 인스턴스 만 메모리에 저장됩니다! 여기에 메모리 페널티가 없습니다. 문제 ""는를 사용할 때마다 비교 루프가 실행되어 ""이미 인턴 풀에 있는지 확인하는 것 입니다. 다른쪽에 는 .NET Framework 메모리 영역에 저장된 String.Empty 참조가 있습니다 . VB.NET 및 C # 응용 프로그램의 동일한 메모리 주소를 가리 킵니다. 따라서 참조가 있을 때 필요할 때 마다 참조를 검색하는 이유는 무엇입니까?""String.Empty""String.Empty?

참조 : String.Emptyvs""


2
이 때문에 진실되지 않았습니다 .NET 2.0
nelsontruran

6

String.Empty는 오브젝트를 작성하지 않지만 ""는 작성합니다. 그러나 여기서 지적했듯이 그 차이 는 사소한 것입니다.


4
string.Empty 또는 ""에 대해 문자열을 확인하면 사소하지 않습니다. Eugene Katz가 지적한 것처럼 실제로 String.IsNullOrEmpty를 사용해야합니다. 그렇지 않으면 예기치 않은 결과가 발생합니다.
Sigur

6

""의 모든 인스턴스는 동일하고 인터 닝 된 문자열 리터럴입니다 (또는 있어야합니다). 따라서 ""를 사용할 때마다 힙에 새 객체를 던지지 않고 동일한 인터 닝 된 객체에 대한 참조를 만듭니다. 그렇게 말하면서, 나는 string.Empty를 선호합니다. 코드를 더 읽기 쉽게 만든다고 생각합니다.



4
string mystring = "";
ldstr ""

ldstr 메타 데이터에 저장된 문자열 리터럴에 새 객체 참조를 푸시합니다.

string mystring = String.Empty;
ldsfld string [mscorlib]System.String::Empty

ldsfld 정적 필드의 값을 평가 스택으로 푸시

IMHO가 더 명확하고 덜 VB-ish이기 때문에 String.Empty대신 사용 하는 경향이 있습니다 "".


3

Eric Lippert 는 다음과 같이 썼습니다 (2013 년 6 월 17 일).
"C # 컴파일러에서 처음으로 작업 한 알고리즘은 문자열 연결을 처리하는 최적화 프로그램이었습니다. 불행히도 저는이 최적화를 Roslyn 코드베이스로 이식하지 못했습니다. " 그것을 얻으십시오! "

2019 년 1 월 현재 일부 Roslyn x64 결과 는 다음과 같습니다 .이 페이지의 다른 답변에 대한 합의 된 의견에도 불구하고, 현재 x64 JIT가 모든 사례를 말하고 완료했을 때 이러한 모든 사례를 동일하게 취급하는 것으로 보이지 않습니다.

그러나 특히이 예제 중 하나만 실제로 호출 String.Concat하고 결과가 정확하지 않기 때문에 (최적화 감독과 달리) 추측합니다. 다른 차이점은 설명하기가 더 어려워 보입니다.


default (String) + {default (String), "", String.Empty}

static String s00() => default(String) + default(String);
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s01() => default(String) + "";
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s02() => default(String) + String.Empty;
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

""+ {default (String), "", String.Empty}

static String s03() => "" + default(String);
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s04() => "" + "";
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s05() => "" + String.Empty;
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

String.Empty + {default (String), "", String.Empty}

static String s06() => String.Empty + default(String);
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

static String s07() => String.Empty + "";
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

static String s08() => String.Empty + String.Empty;
    mov  rcx,[String::Empty]
    mov  rcx,qword ptr [rcx]
    mov  qword ptr [rsp+20h],rcx
    mov  rcx,qword ptr [rsp+20h]
    mov  rdx,qword ptr [rsp+20h]
    call F330CF60                 ; <-- String.Concat
    nop
    add  rsp,28h
    ret


테스트 세부 사항

Microsoft (R) Visual C# Compiler version 2.10.0.0 (b9fb1610)
AMD64 Release
[MethodImpl(MethodImplOptions.NoInlining)]
'SuppressJitOptimization' = false

2

Entity Framework 관점에서이 기능을 사용하면 EF 버전 6.1.3이 유효성 검사시 String.Empty와 ""를 다르게 처리하는 것으로 보입니다.

string.Empty는 유효성 검증을 위해 널값으로 처리되며 필수 (속성) 필드에서 유효성 검증 오류가 사용되면 유효성 검증 오류가 발생합니다. 여기서 ""는 유효성 검사를 통과하고 오류를 발생시키지 않습니다.

이 문제는 EF 7+에서 해결 될 수 있습니다. 참조 :-https: //github.com/aspnet/EntityFramework/issues/2610 ).

편집 : [Required (AllowEmptyStrings = true)]는이 문제를 해결하여 string.Empty의 유효성을 검사합니다.


2

String.Empty는 컴파일 타임 상수가 아니므로 함수 정의에서 기본값으로 사용할 수 없습니다.

public void test(int i=0,string s="")
    {
      // Function Body
    }

1
이 답변의 요약 : public void test(int i=0, string s=string.Empty) {}컴파일하지 말고 " 's의 기본 매개 변수 값은 컴파일 타임 상수 여야합니다. OP의 답변이 작동합니다.
bugybunny

1

코드를 통해 시각적으로 스캔 할 때 문자열 색상이 지정되는 방식으로 ""이 색상으로 표시됩니다. string.Empty는 일반 클래스 멤버 액세스처럼 보입니다. 간략히 살펴보면 ""를 쉽게 발견하거나 의미를 직관 할 수 있습니다.

문자열을 찾으십시오 (스택 오버플로 색상 지정은 정확히 도움이되지는 않지만 VS에서는 더 분명합니다).

var i = 30;
var f = Math.Pi;
var s = "";
var d = 22.2m;
var t = "I am some text";
var e = string.Empty;

2
좋은 지적을했지만 개발자가 실제로 ""를 의미 했습니까? 아마도 그들은 아직 알려지지 않은 가치를두고 돌아 오는 것을 잊었을 것입니까? string.Empty는 원래 작성자가 실제로 string.Empty를 의미했음을 확신 할 수있는 이점이 있습니다. 내가 아는 사소한 점입니다.
mark_h 2014 년

또한 악의적 인 (또는 부주의 한) 개발자는와 같은 적절한 이스케이프 시퀀스를 사용하는 대신 따옴표 사이에 0 너비 문자를 넣을 수 있습니다 \u00ad.
Palec

-8

여기의 모든 사람들은 좋은 이론적 설명을했습니다. 나는 비슷한 의심을했다. 그래서 기본 코딩을 시도했습니다. 그리고 나는 차이점을 발견했다. 차이점이 있습니다.

string str=null;
Console.WriteLine(str.Length);  // Exception(NullRefernceException) for pointing to null reference. 


string str = string.Empty;
Console.WriteLine(str.Length);  // 0

따라서 "Null"은 절대로 void & "String.Empty"는 일종의 값을 포함하지만 비어 있음을 의미합니다.


7
질문은 ""대 에 관한 것 string.Empty입니다. 문자열이 비어 있는지 확인하려고 할 때만 null언급되었습니다.
Palec
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.