Tại sao nó hợp lệ để nối chuỗi Null nhưng không gọi được null null.ToString () thì sao?


126

Đây là mã C # hợp lệ

var bob = "abc" + null + null + null + "123";  // abc123

Đây không phải là mã C # hợp lệ

var wtf = null.ToString(); // compiler error

Tại sao tuyên bố đầu tiên hợp lệ?


97
Tôi thấy thật kỳ lạ khi bạn null.ToString()được đặt tên wtf. Tại sao điều đó làm bạn ngạc nhiên? Bạn không thể gọi một phương thức cá thể khi bạn không có gì để gọi nó từ vị trí đầu tiên.
BoltClock

11
@BoltClock: Tất nhiên có thể gọi một phương thức cá thể trên một cá thể null. Không thể có trong C #, nhưng rất hợp lệ trên CLR :)
leppie

1
Các câu trả lời liên quan đến String.Concat gần như đúng. Trong thực tế, ví dụ cụ thể trong câu hỏi là một lần gập liên tục và các giá trị rỗng trong dòng đầu tiên bị loại bỏ bởi trình biên dịch - tức là chúng không bao giờ được đánh giá trong thời gian chạy vì chúng không tồn tại nữa - trình biên dịch đã xóa chúng. Tôi đã viết một đoạn độc thoại nhỏ về tất cả các quy tắc nối và nối chuỗi liên tục ở đây để biết thêm thông tin: stackoverflow.com/questions/9132338/ Lỗi .
Chris Shain

77
bạn có thể thêm không có gì vào một chuỗi nhưng bạn không thể tạo ra một chuỗi từ không có gì.
RGB

Là tuyên bố thứ hai không hợp lệ? class null_extension { String ToString( Object this arg ) { return ToString(arg); } }
ctrl-alt-delor

Câu trả lời:


163

Lý do cho lần đầu tiên làm việc:

Từ MSDN :

Trong các hoạt động nối chuỗi, trình biên dịch C # xử lý chuỗi null giống như chuỗi rỗng, nhưng nó không chuyển đổi giá trị của chuỗi null gốc.

Thêm thông tin về toán tử nhị phân + :

Toán tử nhị phân + thực hiện nối chuỗi khi một hoặc cả hai toán hạng thuộc kiểu chuỗi.

Nếu một toán hạng của nối chuỗi là null, một chuỗi rỗng được thay thế. Mặt khác, bất kỳ đối số không phải chuỗi nào cũng được chuyển đổi thành biểu diễn chuỗi của nó bằng cách gọi ToStringphương thức ảo được kế thừa từ đối tượng kiểu.

Nếu ToStringtrả về null, một chuỗi rỗng được thay thế.

Lý do của lỗi thứ hai là:

null (Tham chiếu C #) - Từ khóa null là một nghĩa đen đại diện cho một tham chiếu null, một từ khóa không tham chiếu đến bất kỳ đối tượng nào. null là giá trị mặc định của các biến kiểu tham chiếu.


1
Điều này sẽ làm việc sau đó? var wtf = ((String)null).ToString();Tôi đang làm việc ở Java gần đây, nơi có thể truyền null, đã được một thời gian kể từ khi tôi làm việc với C #.
Joool

14
@Jochem: Bạn vẫn đang cố gắng gọi một phương thức trên một đối tượng null, vì vậy tôi đoán là không. Nghĩ về nó như null.ToString()vs ToString(null).
Svish

@Svish vâng, bây giờ tôi nghĩ lại, nó là một đối tượng null, vì vậy bạn nói đúng, nó sẽ không hoạt động. Nó cũng không có trong Java: ngoại lệ con trỏ null. Đừng bận tâm. Tnx cho câu trả lời của bạn! [chỉnh sửa: đã thử nghiệm nó trong Java: NullPulumException. Với sự khác biệt với cast nó biên dịch, mà không cast nó không].
Joool

nói chung không có lý do để cố tình concat hoặc ToString từ khóa null. Nếu bạn cần một chuỗi rỗng, hãy sử dụng chuỗi.Empty. nếu bạn cần kiểm tra xem một biến chuỗi có phải là null hay không, bạn có thể sử dụng (myString == null) hoặc string.IsNullOrEmpty (myString). Ngoài ra, để chuyển đổi một biến chuỗi null thành chuỗi.Empty sử dụng myNewString = (myString == null ?? string.Empty)
csauve

@Jochem truyền nó sẽ làm cho nó biên dịch nhưng nó thực sự sẽ ném NullReferenceException vào thời gian chạy
jeroenh

108

Bởi vì +toán tử trong C # dịch bên trong String.Concat, đây là một phương thức tĩnh. Và phương pháp này xảy ra để coi nullnhư một chuỗi rỗng. Nếu bạn nhìn vào nguồn của String.ConcatReflector, bạn sẽ thấy nó:

// while looping through the parameters
strArray[i] = (str == null) ? Empty : str;
// then concatenate that string array

(MSDN cũng đề cập đến nó: http://msdn.microsoft.com/en-us/l Library / k9c94ey1.aspx )

Mặt khác, ToString()là một phương thức cá thể, mà bạn không thể gọi null(phương thức nào nên được sử dụng cho null?).


2
Không có toán tử + nào trong lớp String mặc dù vậy .. Nó có phải là trình biên dịch dịch sang String.Concat không?
siêu thực

@superlogical Vâng, đó là những gì trình biên dịch làm. Vì vậy, thực sự, +toán tử trên chuỗi trong C # chỉ là cú pháp đường cho String.Concat.
Botz3000

Thêm vào đó: Trình biên dịch tự động chọn quá tải của Concat có ý nghĩa nhất. Quá tải có sẵn là 1, 2 hoặc 3 tham số đối tượng, 4 tham số đối tượng + __arglistparamsphiên bản mảng đối tượng.
Wormbo

Mảng chuỗi nào đang được sửa đổi ở đó? Có phải Concatluôn tạo một mảng mới của các chuỗi không null ngay cả khi được cung cấp một chuỗi các chuỗi không null hoặc có điều gì khác đang xảy ra không?
supercat

@supercat Mảng sửa đổi là một mảng mới, sau đó được truyền cho một phương thức trợ giúp riêng. Bạn có thể tự nhìn vào mã bằng phản xạ.
Botz3000

29

Mẫu đầu tiên sẽ được dịch thành:

var bob = String.Concat("abc123", null, null, null, "abs123");

Các Concatphương pháp kiểm tra đầu vào và dịch null như một chuỗi rỗng

Mẫu thứ hai sẽ được dịch thành:

var wtf = ((object)null).ToString();

Vì vậy, một nullngoại lệ tham chiếu sẽ được tạo ra ở đây


Trên thực tế, a AccessViolationExceptionsẽ bị ném :)
leppie

Tôi vừa mới làm :) ((object)null).ToString()=> AccessViolation ((object)1 as string).ToString()=>NullReference
leppie

1
Tôi đã có NullReferenceException. DN 4.0
Viacheslav Smityukh

Hấp dẫn. Khi tôi đặt ((object)null).ToString()bên trong try/catchtôi cũng nhận được NullReferenceException.
leppie

3
@Ieppie ngoại trừ việc không ném AccessViolation (tại sao lại như vậy?) - NullReferenceException ở đây.
jeroenh

11

Phần đầu tiên của mã của bạn chỉ được xử lý như vậy trong String.Concat,

đó là những gì trình biên dịch C # gọi khi bạn thêm chuỗi. " abc" + nullđược dịch sang String.Concat("abc", null),

và bên trong, phương pháp đó thay thế null bằng String.Empty. Vì vậy, đó là lý do tại sao phần đầu tiên của mã không có ngoại lệ. nó giống như

var bob = "abc" + string.Empty + string.Empty + string.Empty + "123";  //abc123

Và trong phần 2 của mã của bạn đưa ra ngoại lệ vì 'null' không phải là một đối tượng, từ khóa null là một nghĩa đen đại diện cho một tham chiếu null , một từ không tham chiếu đến bất kỳ đối tượng nào. null là giá trị mặc định của các biến kiểu tham chiếu.

Và ' ToString()' là một phương thức có thể được gọi bằng một thể hiện của một đối tượng nhưng không phải là bất kỳ nghĩa đen nào.


3
String.Emptykhông bằng null. Nó chỉ được đối xử theo cùng một cách trong một số phương pháp như String.Concat. Ví dụ: nếu bạn có một biến chuỗi được đặt thành null, C # sẽ không thay thế nó String.Emptynếu bạn cố gắng gọi một phương thức trên nó.
Botz3000

3
Hầu hết. nullkhông được xử lý như String.EmptyC #, nó chỉ được xử lý như vậy String.Concat, đó là những gì trình biên dịch C # gọi khi bạn thêm chuỗi. "abc" + nullđược dịch sang String.Concat("abc", null), và bên trong, phương thức đó thay thế nullbằng String.Empty. Phần thứ hai là hoàn toàn chính xác.
Botz3000

9

Trong khung COM có trước .net, cần có bất kỳ thường trình nào nhận được một chuỗi để giải phóng nó khi nó được thực hiện với nó. Bởi vì rất phổ biến khi các chuỗi rỗng được truyền vào và ra khỏi các thường trình và vì cố gắng "giải phóng" một con trỏ null được định nghĩa là một hoạt động không có gì hợp pháp, Microsoft đã quyết định có một con trỏ chuỗi rỗng đại diện cho một chuỗi rỗng.

Để cho phép một số khả năng tương thích với COM, nhiều thói quen trong .net sẽ diễn giải một đối tượng null là một đại diện hợp pháp dưới dạng một chuỗi rỗng. Với một vài thay đổi nhỏ .net và các ngôn ngữ của nó (đáng chú ý nhất là cho phép các thành viên thể hiện biểu thị "không được gọi là ảo"), Microsoft có thể đã làm cho nullcác đối tượng của String kiểu khai báo hoạt động giống như các chuỗi rỗng. Nếu Microsoft đã làm điều đó, thì họ cũng sẽ phải thực hiện Nullable<T>công việc hơi khác một chút (để cho phép - một Nullable<String>cái gì đó họ nên IMHO đã làm bằng mọi cách) và / hoặc xác định một NullableStringloại có thể hoán đổi cho nhau String, nhưng sẽ không liên quan đến nullnhư một chuỗi rỗng hợp lệ.

Như vậy, có một số bối cảnh trong đó a nullsẽ được coi là một chuỗi trống hợp pháp và các bối cảnh khác trong đó nó sẽ không. Không phải là một tình huống hữu ích khủng khiếp, nhưng một tình huống mà các lập trình viên nên nhận thức được. Nói chung, các biểu thức của biểu mẫu stringValue.someMembersẽ thất bại nếu stringValuenull, nhưng hầu hết các phương thức và toán tử khung chấp nhận các chuỗi làm tham số sẽ coi nulllà một chuỗi rỗng.


5

'+'là một toán tử infix. Giống như bất kỳ toán tử nào, nó thực sự đang gọi một phương thức. Bạn có thể tưởng tượng một phiên bản không phải là infix"wow".Plus(null) == "wow"

Người thực hiện đã quyết định một cái gì đó như thế này ...

class String
{
  ...
  String Plus(ending)
  {
     if(ending == null) return this;
     ...
  }
} 

Vì vậy, ví dụ của bạn trở thành

var bob = "abc".Plus(null).Plus(null).Plus(null).Plus("123");  // abc123

giống như

var bob = "abc".Plus("123");  // abc123

Không có lúc nào null trở thành một chuỗi. Vì vậy, null.ToString()không có gì khác nhau null.VoteMyAnswer(). ;)


Thực sự, nó giống như var bob = Plus(Plus(Plus(Plus("abc",null),null),null),"123");. Quá tải toán tử là các phương thức tĩnh tại trung tâm của chúng: msdn.microsoft.com/en-us/l Library / s53ehcz3 (v = VS.71) .aspx Chúng không, var bob = null + "abc";hoặc đặc biệt string bob = null + null;sẽ không hợp lệ.
Ben Mosher

Bạn nói đúng, tôi chỉ cảm thấy mình sẽ quá bối rối nếu tôi giải thích nó theo cách đó.
Nigel Thorne

3

Tôi đoán bởi vì đó là một nghĩa đen mà không đề cập đến bất kỳ đối tượng. ToString()cần một object.


3

Ai đó đã nói trong chủ đề thảo luận này rằng bạn không thể tạo ra một chuỗi từ không có gì. (đó là một cụm từ tốt đẹp như tôi nghĩ). Nhưng có - bạn có thể :-), như ví dụ sau đây cho thấy:

var x = null + (string)null;     
var wtf = x.ToString();

hoạt động tốt và không ném một ngoại lệ nào cả. Sự khác biệt duy nhất là bạn cần bỏ một trong các null thành một chuỗi - nếu bạn loại bỏ (chuỗi) , thì ví dụ vẫn biên dịch, nhưng ném một ngoại lệ thời gian chạy: "Toán tử '+' không rõ ràng trên các toán hạng của nhập '<null>' và '<null>' ".

NB Trong ví dụ mã ở trên, giá trị của x không phải là null như bạn mong đợi, nó thực sự là một chuỗi rỗng sau khi bạn đã chuyển một trong các toán hạng thành một chuỗi.


Một sự thật thú vị khác là trong C # / .NET , cách nullxử lý không phải lúc nào cũng giống nhau nếu bạn xem xét các loại dữ liệu khác nhau. Ví dụ:

int? x = 1;  //  string x = "1";
x = x + null + null;
Console.WriteLine((x==null) ? "<null>" : x.ToString());

Xem xét dòng đầu tiên của đoạn mã: Nếu xlà một biến số nguyên không int?có giá trị (nghĩa là ) có chứa giá trị 1, thì bạn sẽ nhận được kết quả <null>trở lại. Nếu đó là một chuỗi (như được hiển thị trong nhận xét) có giá trị "1", thì bạn sẽ nhận "1"lại chứ không phải là<null> .

NB Cũng thú vị: Nếu bạn đang sử dụng var x = 1;cho dòng đầu tiên, thì bạn đang gặp lỗi thời gian chạy. Tại sao? Bởi vì phép gán sẽ biến biến xthành kiểu dữ liệu int, không phải là null. Trình biên dịch không giả định int?ở đây, và do đó thất bại trong dòng thứ 2 nullđược thêm vào.


2

Thêm nullvào một chuỗi chỉ đơn giản là bỏ qua. null(trong ví dụ thứ hai của bạn) không phải là một thể hiện của bất kỳ đối tượng nào, vì vậy nó thậm chí không có một ToString()phương thức. Nó chỉ là một nghĩa đen.


1

Bởi vì không có sự khác biệt giữa string.Emptynullkhi bạn nối chuỗi. Bạn có thể chuyển null vào string.Formatlà tốt. Nhưng bạn đang cố gắng gọi một phương thức trên null, điều này sẽ luôn dẫn đến một lỗi NullReferenceExceptionvà do đó tạo ra lỗi trình biên dịch.
Nếu vì lý do nào đó bạn thực sự muốn làm điều đó, bạn có thể viết một phương thức mở rộng, kiểm tra nullvà sau đó trả về string.Empty. Nhưng một phần mở rộng như thế chỉ nên được sử dụng khi cần thiết tuyệt đối (theo ý kiến ​​của tôi).


0

Như chung: Có thể hoặc không thể hợp lệ chấp nhận null làm tham số tùy thuộc vào đặc điểm kỹ thuật, nhưng luôn luôn không hợp lệ khi gọi một phương thức trên null.


Đó là chủ đề khác tại sao toán hạng của toán tử + có thể là null trong trường hợp chuỗi. Đây là điều VB (xin lỗi các bạn) để làm cho cuộc sống lập trình viên dễ dàng hơn, hoặc giả sử lập trình viên không thể đối phó với null. Tôi hoàn toàn không đồng ý đặc điểm kỹ thuật này. 'Không biết' + 'bất cứ điều gì' nên vẫn là 'chưa biế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.