Sự khác biệt giữa String.Empty và (Đầm trống) là gì?


288

Trong .NET, sự khác biệt giữa String.Empty"", và chúng có thể hoán đổi cho nhau không, hoặc có một số vấn đề tham chiếu hoặc Bản địa hóa cơ bản xung quanh sự bình đẳng String.Emptysẽ đảm bảo không phải là vấn đề?


2
Câu hỏi thực sự KHÔNG phải là cái gì hơn tại sao . Tại sao Microsoft đưa ra string.Emptyvà lý do đằng sau tuyên bố nó là gì readonlythay vì const.
dùng1451111

Câu trả lời:


294

Trong .NET trước phiên bản 2.0, ""tạo một đối tượng trong khi string.Emptykhông tạo đối tượng ref , điều này làm cho string.Emptyhiệu quả hơn.

Trong phiên bản 2.0 trở lên của .NET, tất cả các lần xuất hiện ""đều đề cập đến cùng một chuỗi ký tự, có nghĩa ""là tương đương .Empty, nhưng vẫn không nhanh như .Length == 0.

.Length == 0là tùy chọn nhanh nhất, nhưng .Emptylàm cho mã sạch hơn một chút.

Xem thông số kỹ thuật .NET để biết thêm thông tin .


91
"" sẽ chỉ tạo một đối tượng một lần nào đó do thực tập chuỗi. Về cơ bản sự đánh đổi hiệu suất là đậu phộng - khả năng đọc là quan trọng hơn.
Jon Skeet

12
Điều thú vị là mặc dù câu hỏi không nói gì về việc so sánh một chuỗi với "" hoặc chuỗi.Empty để kiểm tra các chuỗi trống, rất nhiều người dường như giải thích câu hỏi theo cách đó ...
peSHIr

11
Tôi sẽ cẩn thận với .Lạng == 0, vì nó có thể đưa ra một ngoại lệ nếu biến chuỗi của bạn là null. Nếu bạn kiểm tra nó với "", nó sẽ trả về false đúng mà không có ngoại lệ.
Jeffrey Harmon

11
@JeffreyHarmon: Hoặc bạn có thể sử dụng string.IsNullOrEmpty( stringVar ).
Flynn1179

1
Nếu bất cứ ai muốn ngăn chặn các thực tiễn xấu để kiểm tra chuỗi trống, bạn có thể bật CA1820 trong Phân tích mã. docs.microsoft.com/visualstudio/code-quality/ từ
sean

197

sự khác biệt giữa String.Empty và "" là gì và chúng có thể hoán đổi cho nhau không

string.Emptylà trường chỉ đọc trong khi đó ""là hằng số thời gian biên dịch. Những nơi họ cư xử khác nhau là:

Giá trị tham số mặc định trong C # 4.0 trở lên

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

Biểu hiện trường hợp trong câu lệnh switch

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

    case "":
        break;

}

Đối số thuộc tính

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

21
Thật thú vị, tôi không nghĩ câu hỏi cũ này sẽ nhận được bất kỳ thông tin mới nào có liên quan. Tôi đã sai
johnc

Tôi nghĩ rằng giá trị tham số mặc định số 1 của bạn trong C # 4.0 trở lên về cơ bản là trùng lặp với các đối số thuộc tính ví dụ # 3 của bạn , vì tôi tin rằng .NET triển khai các tham số mặc định thành các thuộc tính. Vì vậy, về cơ bản hơn, bạn chỉ đơn giản là không thể đặt một "giá trị" (thời gian chạy) (trong trường hợp này, một trình xử lý cá thể , cho các trình điều khiển ngoài đó) vào siêu dữ liệu (thời gian biên dịch).
Glenn Slayden

@GlennSlayden, tôi không đồng ý với bạn. Khởi tạo thuộc tính khác với khởi tạo thông thường. Đó là bởi vì bạn có thể vượt qua String.Emptynhư một đối số trong hầu hết các trường hợp. Bạn đúng rằng tất cả các ví dụ cho thấy điều đó you simply can't put a (run-time) "value" into (compile-time) metadatavà đó là những gì các ví dụ đang nhắm đến để hiển thị.
Uchiha Sasuke

42

Các câu trả lời trước đó là chính xác cho .NET 1.1 (xem ngày của bài đăng mà họ liên kết: 2003). Kể từ .NET 2.0 trở lên, về cơ bản không có sự khác biệt. JIT cuối cùng sẽ tham chiếu cùng một đối tượng trên heap.

Theo thông số kỹ thuật của C #, phần 2.4.4.5: http://msdn.microsoft.com/en-us/l Library / aa691090 (VS.71) .aspx

Mỗi chuỗi ký tự không nhất thiết dẫn đến một thể hiện chuỗi mới. Khi hai hoặc nhiều chuỗi ký tự chuỗi tương đương theo toán tử đẳng thức chuỗi (Mục 7.9.7) xuất hiện trong cùng một cụm, các chuỗi ký tự chuỗi này tham chiếu đến cùng một thể hiện chuỗi.

Thậm chí có người còn đề cập đến điều này trong các bình luận trong bài đăng của Brad Abram

Tóm lại, kết quả thực tế của "" so với String.Empty là không. JIT sẽ tìm ra nó cuối cùng.

Cá nhân tôi đã phát hiện ra rằng JIT thông minh hơn tôi rất nhiều và vì vậy tôi cố gắng không quá thông minh với các tối ưu hóa trình biên dịch vi mô như thế. JIT sẽ mở ra các vòng lặp (), loại bỏ mã dự phòng, phương thức nội tuyến, v.v. tốt hơn và vào thời điểm thích hợp hơn so với trình biên dịch I hoặc C # có thể dự đoán trước. Hãy để JIT thực hiện công việc của mình :)


1
Lỗi đánh máy nhỏ? "JIT cuối cùng sẽ tham chiếu cùng một đối tượng trên trợ giúp." Ý bạn là "trên đống"?
Dana

Câu hỏi phổ biến liên quan stackoverflow.com/questions/263191/
Mạnh

36

String.Emptylà một trường chỉ đọc trong khi ""là một const . Điều này có nghĩa là bạn không thể sử dụng String.Emptytrong câu lệnh chuyển đổi vì nó không phải là hằng số.


với sự ra đời của defaulttừ khóa, chúng ta có thể thúc đẩy khả năng đọc mà không cần, ngăn chặn sửa đổi ngẫu nhiên và có hằng số thời gian biên dịch, mặc dù thành thật mà nói, tôi vẫn nghĩ String.Empty dễ đọc hơn mặc định, nhưng gõ chậm hơn
Enzoaeneas

18

Một điểm khác biệt nữa là String.Empty tạo mã CIL lớn hơn. Mặc dù mã để tham chiếu "" và String.Empty có cùng độ dài, trình biên dịch không tối ưu hóa nối chuỗi (xem bài đăng trên blog của Eric Lippert ) cho các đối số String.Empty. Các chức năng tương đương sau

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

tạo IL này

.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 
}

Điểm hợp lệ nhưng ai sẽ làm điều đó? Tại sao tôi nên nối một chuỗi trống vào mục đích nào đó?
Robert S.

@RobertS. Có lẽ chuỗi thứ hai nằm trong một hàm riêng biệt được nội tuyến. Thật hiếm, tôi cấp cho bạn.
Bruno Martinez

3
Nó không phải là hiếm khi sử dụng toán tử ternary:"bar " + (ok ? "" : "error")
cộng sinh

13

Các câu trả lời trên là đúng về mặt kỹ thuật, nhưng những gì bạn có thể thực sự muốn sử dụng, để dễ đọc mã nhất và ít có khả năng có ngoại lệ nhất là String.IsNullOrEmpty (s)


3
Về mặt so sánh bình đẳng, tôi hoàn toàn đồng ý, nhưng câu hỏi cũng là về sự khác biệt giữa hai khái niệm cũng như so sánh
johnc

1
Coi chừng "ít có khả năng xảy ra ngoại lệ" thường có nghĩa là "hầu hết cơ hội tiếp tục nhưng làm điều gì đó sai". Ví dụ: nếu bạn có một đối số dòng lệnh mà bạn đang phân tích cú pháp và ai đó gọi ứng dụng của bạn --foo=$BARthì có lẽ bạn muốn phát hiện ra sự khác biệt giữa chúng mà quên đặt một var env và chúng không chuyển cờ. string.IsNullOrEmptythường là một mùi mã mà bạn chưa xác thực đầu vào của mình đúng cách hoặc đang làm những điều kỳ lạ. Nói chung, bạn không nên chấp nhận các chuỗi trống khi bạn thực sự muốn sử dụng nullhoặc một cái gì đó như loại Có thể / Tùy chọn.
Alastair Maw

11

Tôi có xu hướng sử dụng String.Emptythay ""vì một lý do đơn giản, nhưng không rõ ràng: """"KHÔNG giống nhau, cái đầu tiên thực sự có 16 ký tự có chiều rộng bằng không. Rõ ràng không có nhà phát triển có thẩm quyền nào sẽ đưa các ký tự có độ rộng bằng không vào mã của họ, nhưng nếu họ vào đó, đó có thể là một cơn ác mộng bảo trì.

Ghi chú:

  • Tôi đã sử dụng U + FEFF trong ví dụ này.

  • Không chắc SO có ăn những ký tự đó không, nhưng hãy tự mình thử với một trong nhiều ký tự có độ rộng bằng không

  • Tôi chỉ đến nhờ điều này nhờ https://codegolf.stackexchange.com/


Điều này xứng đáng được nâng cao hơn. Tôi đã thấy vấn đề này xảy ra thông qua tích hợp hệ thống trên các nền tảng.
EvilDr

8

Sử dụng String.Emptychứ không phải "".

Đây là nhiều hơn cho tốc độ hơn sử dụng bộ nhớ nhưng nó là một mẹo hữu ích. Đây ""là một nghĩa đen vì vậy sẽ hoạt động như một nghĩa đen: trong lần sử dụng đầu tiên, nó được tạo ra và cho các lần sử dụng sau, tham chiếu của nó được trả về. Chỉ có một phiên bản ""sẽ được lưu trữ trong bộ nhớ cho dù chúng tôi sử dụng bao nhiêu lần! Tôi không thấy bất kỳ hình phạt bộ nhớ ở đây. Vấn đề là mỗi lần ""sử dụng, một vòng lặp so sánh được thực thi để kiểm tra xem cái đó ""đã có trong nhóm thực tập chưa. Mặt khác, String.Empty là một tham chiếu đến vùng ""được lưu trữ trong vùng bộ nhớ .NET Framework . String.Emptyđang trỏ đến cùng một địa chỉ bộ nhớ cho các ứng dụng VB.NET và C #. Vậy tại sao tìm kiếm một tài liệu tham khảo mỗi khi bạn cần "" khi bạn có tài liệu tham khảo đó trongString.Empty?

Tham khảo: String.Emptyvs""


2
Điều này đã không đúng kể từ .net 2.0
nelsontruran

6

String.Empty không tạo đối tượng trong khi "" không. Sự khác biệt, như được chỉ ra ở đây , là tầm thường, tuy nhiên.


4
Sẽ không tầm thường nếu bạn kiểm tra một chuỗi cho chuỗi.Empty hoặc "". Người ta thực sự nên sử dụng String.IsNullOrEmpty như Eugene Katz đã chỉ ra. Nếu không bạn sẽ nhận được kết quả bất ngờ.
Sigur

6

Tất cả các trường hợp của "" là giống nhau, chuỗi ký tự được thực hiện (hoặc chúng phải như vậy). Vì vậy, bạn thực sự sẽ không ném một đối tượng mới vào đống mỗi khi bạn sử dụng "" mà chỉ tạo một tham chiếu đến cùng một đối tượng được thực hiện. Có nói rằng, tôi thích chuỗi.Empty. Tôi nghĩ rằng nó làm cho mã dễ đọc hơn.



4
string mystring = "";
ldstr ""

ldstr đẩy một tham chiếu đối tượng mới đến một chuỗi ký tự được lưu trữ trong siêu dữ liệu.

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

ldsfld đẩy giá trị của trường tĩnh lên ngăn xếp đánh giá

Tôi có xu hướng sử dụng String.Emptythay ""vì bởi vì IMHO rõ ràng hơn và ít VB-ish hơn.


3

Eric Lippert đã viết (ngày 17 tháng 6 năm 2013):
"Thuật toán đầu tiên tôi từng làm trong trình biên dịch C # là trình tối ưu hóa xử lý các chuỗi nối. Thật không may, tôi đã không quản lý được các tối ưu hóa này cho cơ sở mã Roslyn trước khi tôi rời đi; đến đó! "

Dưới đây là một số kết quả Roslyn x64 vào tháng 1 năm 2019. Mặc dù có những nhận xét đồng thuận về các câu trả lời khác trên trang này, nhưng đối với tôi, JIT x64 hiện tại đang xử lý tất cả các trường hợp này một cách chính xác, khi tất cả được nói và thực hiện.

Tuy nhiên, đặc biệt lưu ý rằng chỉ có một trong những ví dụ này thực sự kết thúc cuộc gọi String.Concatvà tôi đoán đó là lý do chính xác tối nghĩa (trái ngược với giám sát tối ưu hóa). Những khác biệt khác có vẻ khó giải thích hơn.


mặc định (Chuỗi) + {mặc định (Chuỗi), "", 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

"" + {mặc định (Chuỗi), "", Chuỗi.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

Chuỗi.Empty + {mặc định (Chuỗi), "", 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


Chi tiết kiểm tra

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

2

Xuất phát từ quan điểm Khung thực thể: Các phiên bản EF 6.1.3 xuất hiện để xử lý String.Empty và "" khác nhau khi xác thực.

chuỗi.Empty được coi là giá trị null cho mục đích xác thực và sẽ đưa ra lỗi xác thực nếu nó được sử dụng trên trường Bắt buộc (được quy); trong đó "" sẽ vượt qua xác nhận và không ném lỗi.

Vấn đề này có thể được giải quyết trong EF 7+. Tham khảo: - https://github.com/aspnet/EntityFramework/issues/2610 ).

Chỉnh sửa: [Bắt buộc (AllowEmptyStrings = true)] sẽ giải quyết vấn đề này, cho phép chuỗi.Empty xác thực.


2

Vì String.Empty không phải là hằng số thời gian biên dịch, bạn không thể sử dụng nó làm giá trị mặc định trong định nghĩa hàm.

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

1
Chỉ là tóm tắt từ câu trả lời này: public void test(int i=0, string s=string.Empty) {}sẽ không biên dịch và nói "Giá trị tham số mặc định cho 's' phải là hằng số thời gian biên dịch. Câu trả lời của OP hoạt động.
bugybunny

1

Khi bạn đang quét trực quan thông qua mã, "" sẽ xuất hiện màu theo cách các chuỗi được tô màu. chuỗi.Empty trông giống như một truy cập thành viên lớp thông thường. Trong một cái nhìn nhanh, dễ dàng phát hiện ra "" hoặc trực giác nghĩa.

Phát hiện các chuỗi (màu sắc ngăn xếp chồng lên nhau không chính xác, nhưng trong VS thì điều này rõ ràng hơn):

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

2
Bạn đã đưa ra một quan điểm tốt, nhưng nhà phát triển thực sự có nghĩa là ""? Có lẽ họ có ý định đưa vào một số giá trị chưa biết và quên trả lại? chuỗi.Empty có lợi thế là giúp bạn tự tin rằng tác giả ban đầu thực sự có nghĩa là chuỗi.Empty. Đó là một điểm nhỏ tôi biết.
mark_h

Ngoài ra, nhà phát triển độc hại (hoặc bất cẩn) có thể đặt ký tự có độ rộng bằng không giữa các dấu ngoặc kép thay vì sử dụng chuỗi thoát thích hợp như thế nào \u00ad.
Palec

-8

Mọi người ở đây đã đưa ra một số làm rõ lý thuyết tốt. Tôi đã có một nghi ngờ tương tự. Vì vậy, tôi đã thử một mã hóa cơ bản trên nó. Và tôi đã tìm thấy một sự khác biệt. Đây là sự khác biệt.

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


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

Vì vậy, có vẻ như "Null" có nghĩa là hoàn toàn void & "String.Empty" có nghĩa là Nó chứa một số loại giá trị, nhưng nó trống rỗng.


7
Lưu ý rằng câu hỏi là về ""so với string.Empty. Chỉ khi cố gắng để biết nếu chuỗi là trống, nullđã được đề cập.
Palec
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.