Vâng, tôi không biết gì về trường hợp SqlTypes
, nhưng chắc chắn có một số lý do tại sao kỹ thuật thêm một chuyển đổi ngầm giữa DBNull.Value
và giá trị của Nullable<T>
với HasValue = false
sẽ không hoạt động.
Hãy nhớ rằng, DBNull
là một loại tài liệu tham khảo, và mặc dù thực tế rằng Nullable<T>
hành vi như một loại tài liệu tham khảo - bằng cách giả vờ để có thể đưa vào giá trị null
- nó thực sự là một kiểu giá trị, với ngữ nghĩa giá trị.
Cụ thể, có trường hợp cạnh kỳ lạ khi các giá trị loại Nullable<T>
được đóng hộp. Hành vi này được đặt biệt trong thời gian chạy tới các giá trị hộp của loại Nullable<T>
đến phiên bản được đóng hộp là T
, không phải là phiên bản được đóng hộp là Nullable<T>
.
Như the MSDN documentation giải thích nó:
Khi một loại nullable được đóng hộp, bộ thực thi ngôn ngữ chung tự động hộp giá trị cơ bản của Nullable (Of T) đối tượng, không phải là Nullable (Of T) đối tượng riêng của mình. Nghĩa là, nếu thuộc tính HasValue là true, nội dung của thuộc tính Value được đóng hộp. Khi giá trị cơ bản của một kiểu nullable là unboxed, thời gian chạy ngôn ngữ chung tạo ra một cấu trúc Nullable (Of T) mới được khởi tạo với giá trị cơ bản.
Nếu thuộc tính HasValue của loại có thể vô hiệu là sai, kết quả của thao tác boxing là Không có gì. Do đó, nếu một kiểu nullable được đóng hộp được chuyển tới một phương thức mong đợi đối số đối tượng, thì phương thức đó phải được chuẩn bị để xử lý trường hợp đối số là Không có gì. Khi không có gì được unboxed vào một loại nullable, thời gian chạy ngôn ngữ chung tạo ra một cấu trúc Nullable (Of T) mới và khởi tạo tài sản HasValue của nó thành false.
Bây giờ chúng ta đi vào một vấn đề hóc búa: spec ngôn ngữ C# (§ 4.3.2) nói rằng chúng ta không thể sử dụng một chuyển đổi unboxing để chuyển đổi DBNull.Value
vào Nullable<T>
:
Đối với một chuyển đổi unboxing để một loại không thể loại để thành công tại thời gian chạy, giá trị của toán hạng nguồn phải là giá trị rỗng hoặc tham chiếu đến giá trị được đóng hộp của không thể nhập giá trị của không thể loại .Nếu toán hạng nguồn là một tham chiếu đến một đối tượng không tương thích, một số System.InvalidCastException
sẽ bị ném.
Và chúng ta không thể sử dụng một chuyển đổi người dùng định nghĩa để chuyển đổi từ object
để Nullable<T>
, một trong hai, theo § 10.10.3:
Nó không thể trực tiếp xác định lại một được xác định trước chuyển đổi. Do đó, nhà khai thác chuyển đổi không được phép chuyển đổi từ hoặc sang số object
vì chuyển đổi ngầm và rõ ràng đã tồn tại giữa object
và tất cả các loại khác.
OK, bạn hoặc tôi không thể làm điều đó, nhưng Microsoft chỉ có thể sửa đổi thông số và làm cho nó hợp pháp, đúng không? Tôi không nghĩ vậy.
Tại sao? Hãy tưởng tượng trường hợp sử dụng dự định: bạn đã có một số phương thức được chỉ định để trả về object
. Trong thực tế, nó trả về DBNull.Value
hoặc int
. Nhưng làm thế nào trình biên dịch có thể biết điều đó? Tất cả những gì nó biết là phương thức được chỉ định để trả về object
. Và toán tử chuyển đổi được áp dụng phải được chọn tại thời gian biên dịch.
OK, do đó, giả sử rằng có một toán tử phép thuật có thể chuyển đổi từ object
thành Nullable<T>
và trình biên dịch có một số cách để biết khi nào nó có thể áp dụng. (Chúng tôi không muốn nó được sử dụng cho mỗi phương pháp được chỉ định để trả lại object
- bạn nên làm gì nếu phương pháp thực sự trả về một string
?) Nhưng chúng tôi vẫn gặp sự cố: chuyển đổi có thể không rõ ràng! Nếu phương thức trả về hoặc là long
hoặc DBNull.Value
và chúng tôi làm int? v = Method();
, chúng tôi nên làm gì khi phương thức trả về một ô được đóng hộp long
?
Về cơ bản, để thực hiện công việc này như dự định, bạn phải sử dụng tương đương dynamic
để xác định loại khi chạy và chuyển đổi dựa trên loại thời gian chạy. Nhưng sau đó chúng tôi đã phá vỡ một quy tắc khác: vì chuyển đổi thực sự sẽ chỉ được chọn khi chạy, không có gì đảm bảo rằng nó thực sự thành công. Nhưng tiềm ẩn chuyển đổi không được phép ném ngoại lệ.
Vì vậy, tại thời điểm này, nó không chỉ là một thay đổi đối với hành vi được chỉ định của ngôn ngữ, một hiệu suất tiềm năng đáng kể, và trên hết nó có thể ném một ngoại lệ bất ngờ! Điều đó có vẻ như một lý do khá tốt để không thực hiện nó. Nhưng nếu bạn cần thêm một lý do nữa, hãy nhớ rằng mọi tính năng đều bắt đầu từ minus 100 points.
Tóm lại: những gì bạn thực sự muốn ở đây không thể thực hiện được bằng chuyển đổi ngầm. Nếu bạn muốn hành vi của dynamic
, chỉ cần sử dụng dynamic
! Đây không những gì bạn muốn, và đã được thực hiện trong C# 4.0:
object x = 23;
object y = null;
dynamic dx = x;
dynamic dy = y;
int? nx = (int?) dx;
int? ny = (int?) dy;
Console.WriteLine("nx.HasValue = {0}; nx.Value = {1}; ny.HasValue = {2};",
nx.HasValue, nx.Value, ny.HasValue);
@DanielPryden: Ok, vậy bạn muốn được unboxing nó thành một giá trị khác nhau tùy thuộc vào nếu mục tiêu là một giá trị hoặc tham khảo loại? Đó có phải là ý bạn không? – jmoreno
@DanielPryden: Câu hỏi đã được mở lại, hy vọng lần này nó sẽ mở đủ lâu để bạn có thể nhập câu trả lời. – jmoreno
OK, tôi đã viết lại câu trả lời của mình, lần này với các tham chiếu đến spec. Hóa ra nó thực sự tồi tệ hơn tôi nghĩ: tại thời điểm này, tôi khá thuyết phục rằng nó không chỉ là một ý tưởng tồi, nó hoàn toàn không thể. Hy vọng câu trả lời của tôi đáng để chờ đợi! –