ความแตกต่างระหว่าง String.Empty และ“” (สตริงว่าง) คืออะไร?


288

ใน. NET ความแตกต่างระหว่างString.Emptyและ""และเป็นสิ่งที่สามารถแลกเปลี่ยนกันได้หรือมีการอ้างอิงพื้นฐานหรือปัญหาการโลคัลไลซ์เซชันเกี่ยวกับความเสมอภาคที่String.Emptyจะทำให้แน่ใจว่าไม่มีปัญหา


2
คำถามที่แท้จริงไม่ได้เป็นสิ่งที่ค่อนข้างทำไม ทำไม Microsoft ขึ้นมาด้วยstring.Emptyและสิ่งที่เป็นเหตุผลที่อยู่เบื้องหลังการประกาศเป็นแทนreadonly const
user1451111

คำตอบ:


295

ใน. NET ก่อนหน้ารุ่น 2.0 ""สร้างวัตถุในขณะที่string.Emptyสร้างไม่มีการอ้างอิงวัตถุซึ่งทำให้string.Emptyมีประสิทธิภาพมากขึ้น

ในรุ่น 2.0 และหลังจาก .NET, เหตุการณ์ที่เกิดขึ้นทั้งหมดของ""ดูที่ตัวอักษรสายเดียวกันซึ่งหมายความว่า""จะเทียบเท่ากับการแต่ยังคงไม่เป็นอย่างที่.Empty.Length == 0

.Length == 0เป็นตัวเลือกที่เร็วที่สุด แต่.Emptyสร้างรหัสที่ทำความสะอาดได้เล็กน้อย

ดูสเปค NET สำหรับข้อมูลเพิ่มเติม


91
"" จะสร้างวัตถุเพียงครั้งเดียวเนื่องจากการใช้สตริง โดยทั่วไปการแลกเปลี่ยนประสิทธิภาพเป็นถั่วลิสงการอ่านมีความสำคัญมากกว่า
Jon Skeet

12
น่าสนใจว่าแม้ว่าคำถามที่กล่าวว่าไม่มีอะไรเกี่ยวกับการเปรียบเทียบสตริงอย่างใดอย่างหนึ่ง "" หรือ string.Empty เพื่อตรวจสอบสายที่ว่างเปล่าจำนวนมากของคนที่ดูเหมือนจะตีความคำถามที่ทาง ...
peSHIr

11
ฉันจะระมัดระวังด้วย. Length == 0 เนื่องจากอาจทำให้เกิดข้อยกเว้นหากตัวแปรสตริงของคุณเป็นโมฆะ ถ้าคุณตรวจสอบกับ "" มันจะส่งคืนเท็จอย่างถูกต้องโดยไม่มีข้อยกเว้น
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
}

การแสดงออกกรณีในงบสลับ

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

ฉันคิดว่าตัวอย่าง # 1 ค่าพารามิเตอร์เริ่มต้นของคุณใน C # 4.0 หรือสูงกว่านั้นซ้ำซ้อนกับข้อโต้แย้งแอตทริบิวต์ # 3 ของคุณเนื่องจากฉันเชื่อว่า. NET จะปรับใช้พารามิเตอร์เริ่มต้นในแอตทริบิวต์ ดังนั้นโดยพื้นฐานแล้วคุณไม่สามารถใส่ "ค่า" ("เวลาทำงาน)" (ในกรณีนี้ตัวจัดการอินสแตนซ์สำหรับตัวดึงข้อมูลออก) ลงในเมทาดาทา (รวบรวมเวลา)
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) ปรากฏในแอสเซมบลีเดียวกันตัวอักษรสตริงเหล่านี้อ้างอิงถึงอินสแตนซ์ของสตริงเดียวกัน

บางคนถึงกับกล่าวถึงสิ่งนี้ในความคิดเห็นของโพสต์ของแบรดอะรัม

โดยสรุปผลการปฏิบัติของ "" กับ String.Empty คือศูนย์ JIT จะคิดในท้ายที่สุด

ฉันได้ค้นพบแล้วว่า JIT นั้นฉลาดกว่าฉันดังนั้นฉันจึงพยายามไม่ฉลาดเกินกว่าที่จะใช้การเพิ่มประสิทธิภาพไมโครคอมไพเลอร์แบบนั้น JIT จะเปิดสำหรับลูป () ลูป, ลบโค้ดที่ซ้ำซ้อน, วิธีการอินไลน์, ฯลฯ ดีกว่าและในเวลาที่เหมาะสมกว่า I หรือคอมไพเลอร์ C # ที่สามารถคาดการณ์ล่วงหน้าได้ ปล่อยให้ JIT ทำงาน :)


1
พิมพ์ผิดขนาดเล็ก? "JIT จะจบลงด้วยการอ้างอิงวัตถุเดียวกันในความช่วยเหลือ แต่อย่างใด" คุณหมายถึง "on the heap" หรือเปล่า
Dana


36

String.Emptyคืออ่านได้อย่างเดียวฟิลด์ในขณะที่""เป็นconst หมายความว่าคุณไม่สามารถใช้String.Emptyคำสั่ง switch ได้เนื่องจากมันไม่คงที่


ด้วยการถือกำเนิดของdefaultคำหลักที่เราสามารถส่งเสริมการอ่านได้โดยไม่ต้องป้องกันการแก้ไขโดยไม่ได้ตั้งใจและมีค่าคงที่เวลารวบรวมแม้ว่าจะซื่อสัตย์ฉันยังคงคิดว่า String.Empty สามารถอ่านได้มากกว่าค่าเริ่มต้น แต่พิมพ์ช้ากว่า
Enzoaeneas

18

ความแตกต่างก็คือว่า String.Empty สร้างรหัส CIL ที่ใหญ่กว่า ในขณะที่โค้ดสำหรับการอ้างอิง "" และ String.Empty มีความยาวเท่ากันคอมไพเลอร์ไม่ปรับการเรียงสตริงให้เหมาะสม (ดูโพสต์บล็อกของ Eric Lippert ) สำหรับอาร์กิวเมนต์ String.Empty ฟังก์ชั่นเทียบเท่าต่อไปนี้

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.IsNullOrEmpty (s)


3
ในแง่ของการเปรียบเทียบความเท่าเทียมกันฉันเห็นด้วยอย่างสมบูรณ์ แต่คำถามก็เกี่ยวกับความแตกต่างระหว่างแนวคิดทั้งสองรวมถึงการเปรียบเทียบ
johnc

1
ระวังว่า "โอกาสที่น้อยที่สุดของข้อยกเว้น" มักจะหมายถึง "โอกาสส่วนใหญ่ที่จะดำเนินการต่อไป แต่ทำสิ่งที่ผิด" ตัวอย่างเช่นหากคุณมีอาร์กิวเมนต์บรรทัดคำสั่งคุณกำลังแยกวิเคราะห์และบางคนเรียกแอปของคุณด้วย--foo=$BARคุณอาจต้องการเห็นความแตกต่างระหว่างพวกเขาลืมที่จะตั้งค่า env var และพวกเขาไม่ผ่านการตั้งค่าสถานะเลย string.IsNullOrEmptyมักเป็นรหัสกลิ่นที่คุณยังไม่ได้ตรวจสอบอินพุตของคุณอย่างถูกต้องหรือกำลังทำสิ่งแปลก ๆ โดยทั่วไปคุณไม่ควรยอมรับสตริงที่ว่างเปล่าเมื่อคุณต้องการใช้งานnullจริงหรือสิ่งที่ต้องการประเภทตัวเลือกอาจ /
Alastair Maw

11

ฉันมักจะใช้String.Emptyแทน""เหตุผลง่าย ๆ แต่ก็ไม่ชัดเจน: ""และ""ไม่เหมือนกันตัวแรกมีอักขระความกว้าง 16 ศูนย์จริง ๆ เห็นได้ชัดว่าไม่มีนักพัฒนาที่มีความสามารถจะใส่และอักขระความกว้างเป็นศูนย์ในรหัสของพวกเขา แต่ถ้าพวกเขาเข้าไปที่นั่นมันอาจเป็นฝันร้ายของการบำรุงรักษา

หมายเหตุ:

  • ฉันใช้U + FEFFในตัวอย่างนี้

  • ไม่แน่ใจว่า SO จะกินอักขระเหล่านั้นหรือไม่ แต่ลองด้วยตัวคุณเองด้วยหนึ่งในอักขระที่มีความกว้างเป็นศูนย์

  • ฉันเพิ่งมาขอบคุณนี้เพื่อhttps://codegolf.stackexchange.com/


สิ่งนี้สมควรได้รับการโหวตมากขึ้น ฉันเห็นปัญหานี้เกิดขึ้นผ่านการรวมระบบข้ามแพลตฟอร์ม
EvilDr

8

ใช้มากกว่าString.Empty""

นี่คือความเร็วมากกว่าการใช้หน่วยความจำ แต่เป็นเคล็ดลับที่มีประโยชน์ ""เป็นดังนั้นตัวอักษรจะทำหน้าที่เป็นตัวอักษร: ในการใช้งานครั้งแรกมันถูกสร้างขึ้นและต่อไปนี้ใช้อ้างอิงจะถูกส่งกลับ อินสแตนซ์เดียวเท่านั้นที่""จะถูกเก็บไว้ในหน่วยความจำไม่ว่าจะใช้กี่ครั้งก็ตาม! ฉันไม่เห็นบทลงโทษของหน่วยความจำใด ๆ ที่นี่ ปัญหาคือว่าทุกครั้งที่""มีการใช้การวนลูปการเปรียบเทียบจะดำเนินการเพื่อตรวจสอบว่า ""มีอยู่แล้วในสระว่ายน้ำฝึกงาน ในด้านอื่น ๆ ที่String.Empty มีการอ้างอิงไปยัง""ที่เก็บไว้ในโซนหน่วยความจำ .NET Framework String.Emptyกำลังชี้ไปยังที่อยู่หน่วยความจำเดียวกันสำหรับแอปพลิเคชัน VB.NET และ C # ดังนั้นค้นหาเหตุผลสำหรับการอ้างอิงในแต่ละครั้งที่คุณต้องการ"" เมื่อคุณมีที่อ้างอิงในString.Empty?

อ้างอิง: String.Emptyvs""


2
สิ่งนี้ไม่เป็นความจริงตั้งแต่. net 2.0
nelsontruran

6

String.Empty ไม่สร้างวัตถุในขณะที่ "" ทำ อย่างไรก็ตามความแตกต่างดังที่ได้กล่าวไว้ที่นี่เป็นเรื่องเล็กน้อย


4
มันไม่สำคัญถ้าคุณตรวจสอบสตริงสำหรับ string.Empty หรือ "" หนึ่งควรใช้ String.IsNullOrEmpty จริง ๆ ขณะที่ Eugene Katz ชี้ให้เห็น มิฉะนั้นคุณจะได้รับผลลัพธ์ที่ไม่คาดคิด
Sigur

6

อินสแตนซ์ทั้งหมดของ "" เหมือนกันคือสตริงตัวอักษรที่อยู่ภายใน (หรือควรจะเป็น) ดังนั้นคุณจะไม่ได้โยนวัตถุใหม่บนกองทุกครั้งที่คุณใช้ "" แต่เพียงแค่สร้างการอ้างอิงไปยังวัตถุที่อยู่ภายในแบบเดียวกัน ต้องบอกว่าฉันชอบสตริง ฉันคิดว่ามันทำให้โค้ดอ่านง่ายขึ้น



4
string mystring = "";
ldstr ""

ldstr ผลักดันการอ้างอิงวัตถุใหม่ไปยังตัวอักษรสตริงที่เก็บไว้ในเมตาดาต้า

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

ldsfld ผลักดันค่าของสนามคงที่ไปยังกองการประเมินผล

ฉันมักจะใช้String.Emptyแทน""เพราะ IMHO มันชัดเจนขึ้นและน้อยกว่า VB-ish


3

Eric Lippert เขียน (17 มิถุนายน 2013):
"อัลกอริทึมแรกที่ฉันเคยทำงานในคอมไพเลอร์ C # เป็นเครื่องมือเพิ่มประสิทธิภาพที่จัดการกับการเรียงสตริงแต่น่าเสียดายที่ฉันไม่ได้จัดการพอร์ตเหล่านี้ไปยังฐานข้อมูล Roslyn ก่อนที่ฉันจะออกไป ไปที่! "

ต่อไปนี้เป็นผลลัพธ์ของRoslyn x64ณ เดือนมกราคม 2562 แม้จะมีข้อสังเกตที่เป็นเอกฉันท์เกี่ยวกับคำตอบอื่น ๆ ในหน้านี้ แต่ก็ไม่ปรากฏว่าฉันปัจจุบัน 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

"" + {ค่าเริ่มต้น (สตริง), "", 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 + {ค่าเริ่มต้น (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

มาจากจุดนี้ในมุมมองของเอนทิตี้ของเฟรมเวิร์ก: EF เวอร์ชัน 6.1.3 ดูเหมือนจะปฏิบัติกับ String.Empty และ "" แตกต่างกันเมื่อทำการตรวจสอบ

string.Empty ถือว่าเป็นค่า Null สำหรับวัตถุประสงค์ในการตรวจสอบความถูกต้องและจะโยนข้อผิดพลาดในการตรวจสอบความถูกต้องถ้ามันถูกใช้ในช่องที่จำเป็น (ประกอบ) โดยที่ "" จะผ่านการตรวจสอบความถูกต้องและไม่ผิดพลาด

ปัญหานี้อาจแก้ไขได้ใน EF 7+ การอ้างอิง: - https://github.com/aspnet/EntityFramework/issues/2610 )

แก้ไข: [จำเป็น (AllowEmptyStrings = true)] จะแก้ไขปัญหานี้ทำให้สตริงได้รับการตรวจสอบ


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

นอกจากนี้ที่เป็นอันตราย (หรือประมาท) \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" แปลว่าโมฆะ & "String.Empty" หมายถึงมีค่าบางอย่าง แต่ว่างเปล่า


7
โปรดทราบว่าคำถามที่เป็นเรื่องเกี่ยวกับ"" string.Emptyเฉพาะเมื่อพยายามที่จะบอกว่าสตริงว่างเปล่าnullได้รับการกล่าวถึง
Palec
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.