2010-02-01 25 views
59

Rõ ràng, bạn không thể sử dụng mã số null cho khóa, ngay cả khi khóa của bạn là loại có thể bị vô hiệu.Tại sao bạn không thể sử dụng null làm khóa cho Từ điển <bool ?, string>?

Mã này:

var nullableBoolLabels = new System.Collections.Generic.Dictionary<bool?, string> 
{ 
    { true, "Yes" }, 
    { false, "No" }, 
    { null, "(n/a)" } 
}; 

... kết quả trong ngoại lệ này:

Giá trị có thể không được null. Tên thông số: khóa

Mô tả: Một ngoại lệ chưa được xử lý xảy ra trong khi thực hiện yêu cầu web hiện tại. Vui lòng xem lại dấu vết ngăn xếp để biết thêm thông tin về lỗi và vị trí bắt nguồn từ mã.

[ArgumentNullException: Value cannot be null. Parameter name: key] System.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) +44 System.Collections.Generic.Dictionary'2.Insert(TKey key, TValue value, Boolean add) +40
System.Collections.Generic.Dictionary'2.Add(TKey key, TValue value) +13

Tại sao khuôn khổ NET sẽ cho phép một loại nullable cho một chìa khóa, nhưng không cho phép một giá trị null?

+0

Tại sao bạn cần một từ điển cho điều này? Một câu lệnh switch hoàn thành cùng một công việc: 'string getLabel (giá trị bool?) {If (value == null) {...} else if {...} else {...}; } ' – Juliet

+3

@ Juliet, câu chuyện dài, nhưng tôi sẽ cố gắng: Tôi sẽ sử dụng ba nhãn (có, không, n/a) ở ba vị trí khác nhau: (1) một' bool? -> label' converter, (2) công cụ chuyển đổi 'label -> bool?', và (3) 'SelectList' cho' DropDownMenu' (mà tôi vừa chuyển các giá trị từ điển). Theo DRY, tôi muốn các nhãn ở một nơi trong trường hợp sau này tôi đổi ý, nói Y, N, NA. Vì điều đó không được phép, tôi đã kết thúc với ba const (một cho mỗi nhãn) và một mảng chuỗi cho 'SelectList'. Không thuận tiện, nhưng đủ tốt. – devuxer

+0

Đôi khi bạn phải quay lại C++ để xem logic đằng sau việc triển khai trong .NET .. Hãy xem câu trả lời của tôi bên dưới. –

Trả lời

33

Nó sẽ cho bạn biết điều tương tự nếu bạn đã có một Dictionary<SomeType, string>, SomeType là một loại tài liệu tham khảo, và bạn đã cố gắng để vượt qua null như chìa khóa, nó không phải là một cái gì đó chỉ ảnh hưởng đến loại nullable như bool?. Bạn có thể sử dụng bất kỳ loại nào là khóa, có thể vô hiệu hóa hay không.

Tất cả đều dựa trên thực tế là bạn không thể so sánh thực sự với số nulls. Tôi giả định logic đằng sau không thể đặt null trong khóa, một thuộc tính được thiết kế để so sánh với các đối tượng khác là nó làm cho nó không mạch lạc để so sánh các tham chiếu null.

Nếu bạn muốn có lý do từ thông số kỹ thuật, nó sẽ tóm tắt thành "Khóa không thể là tham chiếu null" on MSDN.

Nếu bạn muốn một dụ của một workaround có thể, bạn có thể thử một cái gì đó tương tự như Need an IDictionary implementation that will allow a null key

+4

Sai. Bạn có thể gọi GetHashcode trên Nullable được đặt thành null. Kết quả là 0. –

+0

Điều này không thực sự trả lời câu hỏi. Anh ta muốn biết tại sao Nullable là chấp nhận được khi một trong những giá trị có thể của nó sẽ luôn luôn ném một ngoại lệ. –

+1

Tôi không bao giờ nói bạn không thể gọi 'GetHashCode()' trên một 'Nullable ', tôi nói bạn không thể gọi 'GetHashCode() 'trên' null'. –

-1

Khóa từ điển không thể rỗng trong .NET, bất kể loại khóa nào (có thể có hoặc không có).

Từ MSDN: Miễn là đối tượng được sử dụng làm khóa trong Từ điển < (của < (TKey, TValue>)>), nó không được thay đổi theo bất kỳ cách nào ảnh hưởng đến giá trị băm của nó. Mọi khóa trong từ điển < (của < (TKey, TValue>)>) phải là duy nhất theo so sánh bình đẳng của từ điển. Một khóa không thể là tham chiếu null (Không có gì trong Visual Basic), nhưng một giá trị có thể là, nếu kiểu giá trị TValue là một kiểu tham chiếu. (http://msdn.microsoft.com/en-us/library/xfhwa508.aspx)

+1

Vâng, chúng tôi đã biết điều đó. Câu hỏi đặt ra là tại sao họ chọn để thực hiện nó theo cách đó. –

+0

Câu hỏi dành riêng cho các kiểu nullable và tôi đã nhấn mạnh rằng chúng hoạt động giống như kiểu tham chiếu – jeffora

+0

Chúng không hoạt động giống nhau. Ví dụ, bạn có thể nhận được hascode của một cấu trúc null nhưng không phải là một tham chiếu null. –

0

Giá trị khóa phải là duy nhất, vì vậy null không thể là khóa hợp lệ vì null không cho biết khóa nào.

Đó là lý do tại sao .Net framework không cho phép null giá trị và ném ngoại lệ. Vì lý do tại sao Nullable được phép, và không bắt được thời gian biên dịch, tôi cho rằng lý do là vì mệnh đề where cho phép everyt T ngoại trừ một điều không thể thực hiện được (ít nhất là tôi không biết cách đạt được điều đó) .

+4

Null là một giá trị duy nhất. –

+1

Nhưng theo ý kiến ​​của tôi trong bối cảnh này thì không có ý nghĩa khi hỏi một cuốn từ điển, đưa cho tôi món hàng mà chìa khóa của anh ấy là rỗng. –

+2

Nhìn vào ví dụ của tôi. Điều gì không có ý nghĩa về nó? – devuxer

0

Ah, các vấn đề về mã chung. Hãy xem xét khối này qua Reflector:

private void Insert(TKey key, TValue value, bool add) 
{ 
    int freeList; 
    if (key == null) 
    { 
     ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); 
    } 

Không có cách nào để viết lại mã này để nói "Nullable nulls được phép, nhưng không cho phép loại tham chiếu rỗng".

Ok, vậy làm cách nào để không cho phép TKey là "bool?". Vâng một lần nữa, không có gì trong ngôn ngữ C# cho phép bạn nói điều đó.

+0

typeof (T) .IsValueType sẽ trả về true cho bool? (hoặc bất kỳ kiểu Nullable nào) vì vậy có thể xác định nếu đó là một giá trị nullable so với null tham chiếu. Điều này mang lại cho nó trở thành một quyết định thực hiện – jeffora

12

Thông thường bạn phải quay lại phương pháp và kỹ thuật C++ để hiểu đầy đủ cách thức và lý do .NET Framework hoạt động theo cách cụ thể.

Trong C++, bạn thỉnh thoảng phải chọn một phím sẽ không được sử dụng - từ điển sử dụng phím này để trỏ đến các mục đã xóa và/hoặc trống. Ví dụ: bạn có từ điển là <int, int> và sau khi chèn mục nhập, bạn sẽ xóa nó. Thay vì chạy dọn dẹp rác ngay sau đó và ở đó, tái cơ cấu từ điển, và dẫn đến hiệu suất kém; từ điển sẽ chỉ thay thế giá trị KEY bằng khóa mà bạn đã chọn trước đây, về cơ bản có nghĩa là "khi bạn duyệt qua bộ nhớ từ điển, giả vờ cặp <key,value> này không tồn tại, hãy ghi đè lên nó."

Khóa như vậy cũng được sử dụng trong từ điển phân bổ trước không gian trong nhóm theo cách cụ thể - bạn cần chìa khóa để "khởi tạo" các nhóm thay vì có cờ cho từng mục nhập cho biết có nội dung hay không có giá trị. Vì vậy, thay vì có một số ba <key, value, initialized>, bạn sẽ có một tuple <key, value> với quy tắc là nếu khóa == empty_key thì nó chưa được khởi tạo - và do đó bạn không thể sử dụng empty_key làm giá trị KEY hợp lệ.

Bạn có thể thấy loại hành vi này trong hashtable Google (Từ điển cho bạn NET người :) trong tài liệu ở đây: http://google-sparsehash.googlecode.com/svn/trunk/doc/dense_hash_map.html

Nhìn vào các chức năng set_deleted_keyset_empty_key để có được những gì tôi đang nói về.

Tôi đặt cược .NET sử dụng NULL làm delete_key hoặc empty_key duy nhất để thực hiện các thủ thuật tiện lợi này nhằm cải thiện hiệu suất.

+0

Insightful. Nhưng còn về 'Dictionary 'thì sao? Nó không thể sử dụng 'null' làm khóa và nó chấp nhận' default (int) 'làm khóa; có lẽ nó sử dụng 'Nullable ' nội bộ như là chìa khóa thay vì 'int'? – ANeves

+0

Vâng, int không phải là một loại nullable, vì vậy bạn tự nhiên sẽ không thể sử dụng int (null) như là một chìa khóa. –

+2

... có; hãy để tôi cố gắng nói lại. Vì vậy, những gì sau đó sẽ là "chìa khóa sẽ không được sử dụng" mà sẽ được sử dụng để đánh dấu các mục đã xóa? Trừ khi loại khóa nội bộ là loại giá trị được chọn được đóng vào một 'Nullable ', không có khóa khả dụng vì tất cả các khóa đều hợp lệ. Không? – ANeves

3

Bạn không thể sử dụng một bool null? bởi vì các kiểu nullable có nghĩa là hành động như kiểu tham chiếu. Bạn cũng không thể sử dụng tham chiếu null làm khóa từ điển.

Lý do bạn không thể sử dụng tham chiếu null làm khóa từ điển có thể đi đến quyết định thiết kế tại Microsoft. Cho phép các phím null yêu cầu kiểm tra cho chúng, điều này làm cho việc triển khai chậm hơn và phức tạp hơn. Ví dụ, việc thực hiện sẽ phải tránh sử dụng .Equals hoặc .GetHashCode trên một tham chiếu null.

Tôi đồng ý rằng việc cho phép khóa null sẽ thích hợp hơn, nhưng đã quá muộn để thay đổi hành vi ngay bây giờ. Nếu bạn cần một workaround, bạn có thể viết từ điển của riêng bạn với các phím null được cho phép, hoặc bạn có thể viết một cấu trúc bao bọc mà ngầm chuyển đổi sang/từ T và làm cho khóa-kiểu cho từ điển của bạn (ví dụ:cấu trúc sẽ bọc null và xử lý so sánh và băm, vì vậy từ điển không bao giờ 'thấy' null).

0

Từ điển không thể chấp nhận các loại tham chiếu null vì nhiều lý do, không phải vì chúng không có phương thức GetHashCode.

Giá trị null cho kiểu giá trị rỗng có nghĩa là đại diện cho giá trị rỗng – ngữ nghĩa có nghĩa là đồng nghĩa với giá trị tham chiếu null càng tốt. Nó sẽ hơi kỳ lạ nếu bạn có thể sử dụng giá trị null nullable, nơi bạn không thể sử dụng tham chiếu null chỉ vì một chi tiết thực hiện các kiểu giá trị nullable.

Hoặc đó hoặc từ điển có:

if (key == null) 

và họ không bao giờ thực sự nghĩ về nó.

-1

Tôi vừa mới đọc về điều này; và như Eric trả lời, bây giờ tôi tin rằng điều này là không chính xác, không phải tất cả các chuỗi được tự động Interned, và tôi cần thiết để ghi đè lên các hoạt động bình đẳng.


Điều này bit khi tôi chuyển đổi từ điển sử dụng chuỗi làm khóa thành mảng byte.

Tôi ở trong tư duy của một chuỗi ký tự đơn giản là một mảng ký tự, vì vậy tôi mất một lúc để tìm ra lý do tại sao chuỗi được tạo bằng cách nối làm việc như một khóa để tra cứu trong khi mảng byte được xây dựng trong vòng lặp đã không.

Đó là vì nội bộ .net gán tất cả các chuỗi chứa cùng một giá trị cho cùng một tham chiếu. (Nó được gọi là 'thực tập')

như vậy, sau khi chạy:

{ 
string str1 = "AB"; 
string str2 = "A"; 
str1 += "C"; 
str2 += "BC"; 
} 

str1 và str2 thực trỏ đến vị trí chính xác cùng trong ký ức! làm cho chúng giống nhau; cho phép từ điển tìm một mục được thêm bằng str1 làm khóa bằng cách sử dụng str2.

trong khi nếu bạn:

{ 
char[3] char1; 
char[3] char2; 
char1[0] = 'A'; 
char1[1] = 'B'; 
char1[2] = 'C'; 
char2[0] = 'A'; 
char2[1] = 'B'; 
char2[2] = 'C'; 
} 

CHAR1 và KÝ_TỰ2 là tài liệu tham khảo khác nhau; nếu bạn sử dụng char1 để thêm một mục vào từ điển, bạn không thể sử dụng char2 để tra cứu nó.

+2

Không, điều này là sai. Các chuỗi không được tập trung nếu chúng là kết quả của phép tính. Vấn đề của bạn là byte [] không ghi đè GetHashCode và Equals, vì vậy bạn có thể so sánh tham chiếu, nhưng chuỗi đó, để cung cấp so sánh giá trị. – erikkallen

+0

chắc chắn đó là những gì bạn mong đợi! Chỉ vì hai đối tượng có cùng giá trị không làm cho chúng cùng một đối tượng. Trong chuỗi C# là bất biến nên tất cả các hoạt động thực sự cung cấp cho bạn một chuỗi mới –

2

Dictionairies (mô tả cơ bản)
Một từ điển là generic (gõ) thực hiện của lớp Hashtable, được giới thiệu trong .NET framework 2.0.

Một cửa hàng Hashtable có thể lưu trữ một giá trị dựa trên khóa (cụ thể hơn là mã băm của khóa).
Mọi đối tượng trong .NET đều có phương thức GetHashCode.
Khi bạn chèn một cặp giá trị khóa vào một hashtable, GetHashCode được gọi trên khóa.
Hãy nghĩ về điều đó: bạn không thể gọi phương thức GetHashCode trên null.

Vì vậy, những gì về các loại dễ vỡ?
Lớp Nullable chỉ đơn giản là trình bao bọc, để cho phép gán giá trị null cho các loại giá trị.Về cơ bản, trình bao bọc bao gồm một boolean HasValue cho biết nếu nó là null hay không, và Value để chứa giá trị của loại giá trị.

Kết hợp lại với nhau và bạn nhận được gì
.NET không thực sự quan tâm những gì bạn sử dụng làm khóa trong từ điển bắt buộc.
Nhưng khi bạn thêm một kết hợp giá trị khóa, nó phải có khả năng tạo ra một băm của khóa.
Nó không quan trọng nếu giá trị của bạn được bọc bên trong một Nullable, null.GetHashCode là không thể.

Thuộc tính Trình lập chỉ mục và Thêm phương thức của từ điển sẽ kiểm tra giá trị rỗng và ném một ngoại lệ khi tìm thấy giá trị rỗng.

+0

Nhưng hashcode không, theo định nghĩa, chắc chắn là duy nhất. 'public override int GetHashCode() {return 0; } 'là một thực thi hợp lệ, ngay cả khi khá vô dụng. Vì vậy, nó sẽ không quan trọng nếu từ điển chỉ sử dụng nội bộ một hashcode tùy ý cho các giá trị null. Tôi không tin đây là một lời giải thích tốt. – ANeves

+0

Tuy nhiên null không bằng null. Sử dụng một mã tùy ý cho null sẽ dẫn đến một số mã không thể đoán trước. – Zyphrax

+2

Tôi không đồng ý, tôi tin rằng null bằng null. Nếu 'a == a' * phải * trả về true, thì' null == null' cũng phải trả về true; Không? Ngoài ra, [ví dụ thực hiện] (http://msdn.microsoft.com/en-us/library/ms173147%28v=vs.80%29.aspx#sectionToggle1) được cung cấp trong MSDN trả về true nếu cả hai đều rỗng. Tôi đang suy nghĩ sai về điều này? – ANeves

3

Không có lý do cơ bản nào. HashSet cho phép null và HashSet đơn giản là một từ điển mà khóa có cùng kiểu với giá trị. Vì vậy, thực sự, các phím null nên đã được cho phép nhưng sẽ phá vỡ để thay đổi nó ngay bây giờ vì vậy chúng tôi đang mắc kẹt với nó.

Các vấn đề liên quan