Khởi tạo chuỗi mặc định: NULL hay rỗng? [đóng cửa]


130

Tôi đã luôn khởi tạo các chuỗi của mình thành NULL, với suy nghĩ rằng NULL có nghĩa là không có giá trị và "" hoặc String.Empty là một giá trị hợp lệ. Tôi đã thấy nhiều ví dụ gần đây về mã trong đó String.Empty được coi là giá trị mặc định hoặc không đại diện cho giá trị. Điều này gây cho tôi sự kỳ quặc, với các loại nullable mới được thêm vào trong c #, có vẻ như chúng ta đang có những bước tiến ngược với các chuỗi bằng cách không sử dụng NULL để biểu thị 'Không có giá trị'.

Bạn sử dụng làm trình khởi tạo mặc định là gì và tại sao?

Chỉnh sửa: Dựa trên các câu trả lời tôi tiếp tục suy nghĩ thêm

  1. Tránh xử lý lỗi Nếu giá trị không nên rỗng, tại sao nó lại được đặt NULLở vị trí đầu tiên? Có lẽ sẽ tốt hơn nếu xác định lỗi tại nơi xảy ra thay vì che giấu nó trong phần còn lại của cơ sở mã của bạn?

  2. Tránh kiểm tra null Nếu bạn cảm thấy mệt mỏi khi thực hiện kiểm tra null trong mã, sẽ tốt hơn nếu trừu tượng hóa kiểm tra null? Có lẽ bọc (hoặc mở rộng!) Các phương thức chuỗi để làm cho chúng NULLan toàn? Điều gì xảy ra nếu bạn liên tục sử dụng String.Emptyvà null sẽ hoạt động trên hệ thống của bạn, bạn có bắt đầu thêm NULLkiểm tra không?

Tôi không thể không quay lại với ý kiến ​​rằng đó là sự lười biếng. Bất kỳ DBA nào cũng sẽ tát bạn chín cách để ngớ ngẩn nếu bạn sử dụng '' thay vì nulltrong cơ sở dữ liệu của cô ấy. Tôi nghĩ rằng các nguyên tắc tương tự được áp dụng trong lập trình và nên có ai đó đánh vào những người đứng đầu sử dụng String.Emptythay vì NULLđại diện cho không có giá trị.

Câu hỏi liên quan


"Người lành mạnh"? Tôi không phải là một Dana tôi biết.
vfilby

@Joel, tôi ngạc nhiên bởi có nhiều người không biết gì về Zim hay GIR. Tôi cũng ngạc nhiên về một số người bạn của tôi thấy nó thật đáng ghét. Không nói đó là lòng tốt thuần túy, nhưng có những cốm hài hước tuyệt vời trong đó.
vfilby

Tôi biết, nhưng đôi khi thật vui khi giả vờ khác.
Dana the Sane

1
Tôi gặp vấn đề này trên các bộ sưu tập biểu mẫu MVC hoặc các biến phiên rất nhiều, tôi thấy điều hữu ích nhất là chuyển đổi null thành String.Empty với ?? tốc ký, và sau đó áp dụng bất kỳ hoạt động chuỗi cần thiết. ví dụ. (mục ?? String.Empty) .Trim (). ToUpper ()
sonjz

4
Đây không phải là xây dựng ??
nawfal

Câu trả lời:


111

+1 để phân biệt giữa "trống" và NULL. Tôi đồng ý rằng "trống" có nghĩa là "hợp lệ, nhưng trống" và "NULL" có nghĩa là "không hợp lệ".

Vì vậy, tôi sẽ trả lời câu hỏi của bạn như thế này:

trống khi tôi muốn một giá trị mặc định hợp lệ có thể thay đổi hoặc không thay đổi, ví dụ: tên đệm của người dùng.

NULL khi đó là một lỗi nếu mã tiếp theo không đặt giá trị rõ ràng.


11
Phân biệt giữa NULL và trống là tuyệt vời khi thực sự có một sự khác biệt giữa hai. Tuy nhiên, có nhiều trường hợp không có sự khác biệt và do đó có hai cách thể hiện cùng một điều là trách nhiệm pháp lý.
Greg Smalter

6
@Greg: Mặc dù tôi đồng ý rằng sự đa dạng có khả năng gây nhầm lẫn, nó cũng có thể là một tài sản lớn. Quy ước đơn giản, nhất quán về cách viết "" hoặc NULL để phân biệt giữa các giá trị hợp lệ và không hợp lệ sẽ giúp mã của bạn dễ hiểu hơn. Đây là lý do tại sao tôi luôn kiểm tra booleans với "if (var)," con trỏ với "if (var! = NULL)" và các số nguyên với "if (var! = 0)" - tất cả đều có cùng ý nghĩa với trình biên dịch, nhưng họ mang thêm thông tin giúp nhà phát triển nghèo, người duy trì mã của tôi.
Adam Liss

32

Theo MSDN :

Bằng cách khởi tạo các chuỗi với Emptygiá trị thay vì null, bạn có thể giảm khả năng NullReferenceExceptionxảy ra.

Luôn luôn sử dụng IsNullOrEmpty()là thực hành tốt.


45
Chỉ vì bạn đang giảm cơ hội của ngoại lệ không có nghĩa là ngoại lệ không nên xảy ra. Nếu mã của bạn phụ thuộc vào giá trị ở đó, nó sẽ ném ngoại lệ!
rmeador

1
Chắc chắn, không có tranh luận ở đó. OTOH, nếu bạn chỉ nối các chuỗi lại với nhau ... Tôi nghĩ nó phụ thuộc vào phong cách mã hóa, kinh nghiệm và tình huống.
Tomalak

Đây là chủ yếu những gì tôi sử dụng phân biệt mà cũng sử dụng.
positiveGuy

3
Đừng quên IsNullOrWhiteSpace () cho .NET framework 4+
Coops

13

Tại sao bạn muốn chuỗi của bạn được khởi tạo ở tất cả? Bạn không phải khởi tạo một biến khi bạn khai báo một biến và IMO, bạn chỉ nên làm như vậy khi giá trị bạn đang gán là hợp lệ trong ngữ cảnh của khối mã.

Tôi thấy điều này rất nhiều:

string name = null; // or String.Empty
if (condition)
{
  name = "foo";
}
else
{
  name = "bar";
}

return name;

Không khởi tạo thành null sẽ hiệu quả như vậy. Hơn nữa, hầu hết bạn thường muốn một giá trị được gán. Bằng cách khởi tạo thành null, bạn có thể bỏ lỡ các đường dẫn mã không gán giá trị. Thích như vậy:

string name = null; // or String.Empty
if (condition)
{
  name = "foo";
}
else if (othercondition)
{
  name = "bar";
}

return name; //returns null when condition and othercondition are false

Khi bạn không khởi tạo thành null, trình biên dịch sẽ tạo ra lỗi thông báo rằng không phải tất cả các đường dẫn mã đều gán một giá trị. Tất nhiên, đây là một ví dụ rất đơn giản ...

Matthijs


Trong Visual Studio, nơi tôi tin rằng hầu hết mọi lập trình viên C # đều sử dụng, tình huống thứ hai của bạn (không có = null) sẽ tạo ra một cảnh báo, vì chính xác lý do bạn đã nêu - không quan trọng giá trị mặc định của chuỗi có phải là null hay không. nếu bạn không đảm bảo chuyển nhượng qua mọi đường dẫn mã, IDE (và / hoặc tôi cho rằng trình biên dịch cơ bản [?]) sẽ tạo cảnh báo. Mặc dù các cảnh báo sẽ không ngăn được việc biên dịch, nhưng chúng vẫn ở đó - để lại những giải pháp dễ giải quyết có thể giúp làm xáo trộn những người khác có thể đảm bảo sự chú ý của lập trình viên
Code

theo tôi biết, tình hình đầu tiên sẽ được hoàn toàn hạnh phúc mà không cần khởi tạo của nameđể null(không có cảnh báo), vì mỗi con đường mã gán một giá trị để name- không cần phải khởi tạo có ở tất cả
Mã Jockey

8

Đối với hầu hết các phần mềm không thực sự là phần mềm xử lý chuỗi, logic chương trình không nên phụ thuộc vào nội dung của các biến chuỗi. Bất cứ khi nào tôi thấy một cái gì đó như thế này trong một chương trình:

if (s == "value")

Tôi có một cảm giác tồi tệ. Tại sao có một chuỗi ký tự trong phương pháp này? Cài đặt sgì? Nó có biết rằng logic phụ thuộc vào giá trị của chuỗi không? Nó có biết rằng nó phải là trường hợp thấp hơn để làm việc? Tôi có nên sửa nó bằng cách thay đổi nó để sử dụng String.Comparekhông? Tôi có nên tạo Enumvà phân tích cú pháp không?

Từ quan điểm này, người ta có được một triết lý về mã khá đơn giản: bạn tránh kiểm tra nội dung của chuỗi bất cứ khi nào có thể. So sánh một chuỗi String.Emptythực sự chỉ là một trường hợp đặc biệt so sánh nó với một nghĩa đen: đó là điều cần tránh làm trừ khi bạn thực sự phải làm.

Biết điều này, tôi không chớp mắt khi thấy thứ gì đó như thế này trong cơ sở mã của chúng tôi:

string msg = Validate(item);
if (msg != null)
{
   DisplayErrorMessage(msg);
   return;
}

Tôi biết điều đó Validatesẽ không bao giờ trở lại String.Empty, bởi vì chúng tôi viết mã tốt hơn thế.

Tất nhiên, phần còn lại của thế giới không hoạt động như thế này. Khi chương trình của bạn đang xử lý dữ liệu đầu vào của người dùng, cơ sở dữ liệu, tệp, v.v., bạn phải tính đến các triết lý khác. Ở đó, công việc của mã của bạn là áp đặt trật tự cho sự hỗn loạn. Một phần của thứ tự đó là biết khi nào một chuỗi rỗng nên có nghĩa String.Emptyvà khi nào nó có nghĩa null.

(Để chắc chắn rằng tôi đã không nói ra khỏi mông của mình, tôi chỉ tìm kiếm cơ sở mã của chúng tôi để tìm `String.IsNullOrEmpty '. Tất cả 54 lần xuất hiện của nó đều nằm trong các phương thức xử lý đầu vào của người dùng, trả về giá trị từ tập lệnh Python, kiểm tra các giá trị được lấy từ API bên ngoài, v.v.)


6

Đây thực sự là một lỗ hổng trong ngôn ngữ C #. Không có cách nào để xác định một chuỗi không thể rỗng. Điều này gây ra các vấn đề đơn giản như vấn đề bạn đang mô tả, buộc các lập trình viên phải đưa ra quyết định mà họ không cần phải đưa ra, vì trong nhiều trường hợp, NULL và String.Empty có nghĩa tương tự. Điều đó, đến lượt nó, sau đó có thể buộc các lập trình viên khác phải xử lý cả NULL và String.Empty, điều này gây khó chịu.

Một vấn đề lớn hơn là cơ sở dữ liệu cho phép bạn xác định các trường ánh xạ tới chuỗi C #, nhưng các trường cơ sở dữ liệu có thể được xác định là KHÔNG NULL. Vì vậy, không có cách nào để biểu diễn chính xác, giả sử trường varchar (100) KHÔNG NULL trong SQL Server bằng cách sử dụng loại C #.

Các ngôn ngữ khác, như Spec #, cho phép điều này.

Theo tôi, việc C # không có khả năng xác định chuỗi không cho phép null cũng tệ như việc không thể xác định một int không cho phép null.

Để trả lời hoàn toàn câu hỏi của bạn: Tôi luôn sử dụng chuỗi rỗng để khởi tạo mặc định vì nó giống với cách thức hoạt động của các loại dữ liệu cơ sở dữ liệu. (Chỉnh sửa: Câu lệnh này rất không rõ ràng. Nó nên đọc "Tôi sử dụng chuỗi rỗng để khởi tạo mặc định khi NULL là trạng thái thừa, giống như cách tôi thiết lập cột cơ sở dữ liệu là KHÔNG NULL nếu NULL sẽ là trạng thái thừa. , nhiều cột DB của tôi được thiết lập là KHÔNG NULL, vì vậy khi tôi đưa chúng vào chuỗi C #, chuỗi sẽ trống hoặc có giá trị, nhưng sẽ không bao giờ là NULL. Nói cách khác, tôi chỉ khởi tạo một chuỗi thành NULL nếu null có nghĩa khác với nghĩa của String.Empty và tôi thấy trường hợp đó ít phổ biến hơn (nhưng mọi người ở đây đã đưa ra các ví dụ hợp pháp về trường hợp này). ")


Sử dụng String.Empty chỉ tương tự như một trong những cách mà chuỗi cơ sở dữ liệu được xác định. Sử dụng null để thể hiện không có giá trị nào phù hợp hơn với null nvarchar. Tôi nghĩ rằng bất kỳ DBA nào có giá trị muối của họ sẽ tát bạn chín cách để ngớ ngẩn nếu bạn sử dụng '' để không đại diện cho giá trị.
vfilby

Thật ra, Greg, bạn đã hiểu sai về nó. Đó là các loại giá trị không thể rỗng, ít nhất là "cách các loại cơ sở dữ liệu hoạt động" bởi vì chúng không bao giờ có thể giữ một giá trị null và do đó không bao giờ có thể ánh xạ tới một cột không thể. Trong hợp đồng, bất kỳ chuỗi nào có thể ánh xạ tới bất kỳ cột varchar nào.
Tor Haugen

Bạn nói đúng, khẳng định cuối cùng của tôi không đủ rõ ràng. Hầu hết các lần, các cột cơ sở dữ liệu của tôi KHÔNG phải là NULL (vì sẽ không có sự khác biệt nào giữa ý nghĩa của chuỗi rỗng và NULL), vì vậy tôi cố gắng giữ các chuỗi của mình giống nhau bằng cách không bao giờ lưu trữ null trong đó và đó là ý tôi.
Greg Smalter

5

Nó phụ thuộc.

Bạn có cần phải biết nếu giá trị bị thiếu (có thể không xác định được không)?

Chuỗi trống có phải là giá trị hợp lệ cho việc sử dụng chuỗi đó không?

Nếu bạn trả lời "có" cho cả hai, thì bạn sẽ muốn sử dụng null. Nếu không, bạn không thể nói sự khác biệt giữa "không có giá trị" và "chuỗi rỗng".

Nếu bạn không cần biết nếu không có giá trị thì chuỗi trống có thể an toàn hơn, vì nó cho phép bạn bỏ qua kiểm tra null bất cứ nơi nào bạn sử dụng.



3

Tôi hoặc đặt nó thành "" hoặc null - Tôi luôn kiểm tra bằng cách sử dụng String.IsNullOrEmpty, vì vậy hoặc là tốt.

Nhưng sự đam mê bên trong tôi nói rằng tôi nên đặt nó thành null trước khi tôi có một giá trị phù hợp cho nó ...



2

Có thể đây là một kỹ thuật tránh lỗi (khuyến khích hay không ..)? Vì "" vẫn là một chuỗi, nên bạn có thể gọi các hàm chuỗi trên đó sẽ dẫn đến một ngoại lệ nếu đó là NULL?


1
Đó là cái cớ tôi thường nghe, nghe có vẻ lười biếng. "Tôi không muốn kiểm tra giá trị này vì vậy tôi sẽ đi một lối tắt" là cách nó dường như với tôi.
vfilby

Vâng, tôi không đồng ý. Có thể có một số tình huống trong đó việc giảm số lượng mã kiểm tra lỗi là tốt, nhưng các lệnh gọi hàm không có tác dụng cũng không phải là lớn nhất ..
Dana the Sane

2

Tôi luôn luôn khởi tạo chúng như NULL.

Tôi luôn luôn sử dụng string.IsNullOrEmpty(someString)để kiểm tra giá trị của nó.

Đơn giản.


1

Nó phụ thuộc vào tình hình. Trong hầu hết các trường hợp, tôi sử dụng String.Empty vì tôi không muốn thực hiện kiểm tra null mỗi lần tôi cố gắng sử dụng chuỗi. Nó làm cho mã đơn giản hơn rất nhiều và bạn ít có khả năng đưa ra các sự cố NullReferenceException không mong muốn.

Tôi chỉ đặt chuỗi thành null khi tôi cần biết nó có được đặt hay không và chuỗi trống là thứ hợp lệ để đặt thành chuỗi. Trong thực tế, tôi thấy những tình huống này hiếm gặp.


1

Một chuỗi rỗng là một giá trị (một đoạn văn bản, tình cờ, không xảy ra bất kỳ chữ cái nào). Null biểu thị không có giá trị.

Tôi khởi tạo các biến thành null khi tôi muốn chỉ ra rằng chúng không trỏ đến hoặc chứa các giá trị thực tế - khi mục đích không có giá trị.


1

Lặp lại phản hồi Tomalak, hãy nhớ rằng khi bạn gán biến chuỗi cho giá trị ban đầu là null, biến của bạn không còn là đối tượng chuỗi; giống với bất kỳ đối tượng nào trong C #. Vì vậy, nếu bạn cố gắng truy cập bất kỳ phương thức hoặc thuộc tính nào cho biến của mình và bạn cho rằng đó là một đối tượng chuỗi, bạn sẽ nhận được ngoại lệ NullReferenceException.


1

Null chỉ nên được sử dụng trong trường hợp giá trị là tùy chọn. Nếu giá trị không phải là tùy chọn (như 'Tên' hoặc 'Địa chỉ'), thì giá trị sẽ không bao giờ là null. Điều này áp dụng cho cơ sở dữ liệu cũng như POCO và giao diện người dùng. Null có nghĩa là "giá trị này là tùy chọn và hiện không có."

Nếu trường của bạn không phải là tùy chọn, thì bạn nên khởi tạo nó dưới dạng chuỗi trống. Để khởi tạo nó dưới dạng null sẽ đặt đối tượng của bạn vào trạng thái không hợp lệ (không hợp lệ bởi mô hình dữ liệu của riêng bạn).

Cá nhân tôi muốn thay vào đó là chuỗi không thể rỗng, nhưng thay vào đó chỉ là null nếu chúng ta khai báo "chuỗi?". Mặc dù có lẽ điều này không khả thi hoặc logic ở mức độ sâu hơn; không chắc.


0

Chuỗi không phải là loại giá trị và sẽ không bao giờ ;-)


0

Tôi nghĩ không có lý do gì để không sử dụng null cho giá trị chưa được gán (hoặc tại vị trí này trong luồng chương trình không xảy ra). Nếu bạn muốn phân biệt, có == null. Nếu bạn chỉ muốn kiểm tra một giá trị nhất định và không quan tâm nó là null hay thứ gì khác, String.Equals ("XXX", MyStringVar) sẽ hoạt động tốt.

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.