2012-02-21 41 views
13

Tôi đang sử dụng đoạn mã sau để chuyển đổi một số Json thành một đối tượng động. Khi tôi sử dụng DateTime.Parse trên một thuộc tính của loại động của tôi, tôi sẽ mong đợi var để đoán rằng đó là loại là một DateTime ... thay vào đó, nó vẫn là một động. Điều này không thể đúng, phải không?C# có chọn kiểu sai cho var khi phân tích cú pháp đối tượng động không?

Ví dụ đầy đủ bên dưới.

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json); 

var startDate = DateTime.Parse(settings.startDate); 
var endDate = DateTime.Parse(settings.endDate); 
var userId = int.Parse(settings.userId); 

startDate, endDate và userId vẫn hoạt động, có nghĩa là tôi không thể sử dụng chúng trong biểu thức Lambda sau này. Rõ ràng, tôi có thể sửa mã bằng:

DateTime startDate = DateTime.Parse(settings.startDate); 
DateTime endDate = DateTime.Parse(settings.endDate); 
int userId = int.Parse(settings.userId); 

.. nhưng có vẻ như trình biên dịch đang thực hiện 'phỏng đoán xấu'. bất cứ ai có thể giải thích điều này với tôi?

Cảm ơn

+2

"Mọi người có thể giải thích điều này cho tôi không?" có lẽ là "Liệu Eric Lippert có thể giải thích điều này cho chúng ta không?" – dasblinkenlight

+0

Điều đó làm tôi ngạc nhiên. Tôi không tin bạn cho đến khi tôi thử nó! :-) – Simon

+0

Một cách khác để giải quyết vấn đề này là buộc loại 'string' trên đối số * của' DateTime.Parse() ', như sau:' var startDate = DateTime.Parse ((string) settings.startDate) ' – dasblinkenlight

Trả lời

13

.. nhưng có vẻ như trình biên dịch đang thực hiện 'phỏng đoán xấu'. bất cứ ai có thể giải thích điều này với tôi?

Khi bạn sử dụng dynamic, toàn bộ biểu hiện được điều trị tại thời gian biên dịch như một biểu hiện năng động , gây ra trình biên dịch để điều trị tất cả mọi thứ như năng động và có được thời gian chạy ràng buộc.

này được giải thích trong 7.2 của đặc tả C# Ngôn Ngữ:

Khi không có biểu hiện động có liên quan, C mặc định # để tĩnh ràng buộc, có nghĩa là các loại thời gian biên dịch các biểu thức thành phần được sử dụng trong việc lựa chọn quá trình. Tuy nhiên, khi một trong các biểu thức cấu thành trong các hoạt động được liệt kê ở trên là một biểu thức động, thì hoạt động này thay vì bị ràng buộc động.

này về cơ bản có nghĩa là hầu hết các hoạt động (các loại được liệt kê trong phần 7.2 của spec) mà có bất kỳ yếu tố đó được khai báo là dynamic sẽ được đánh giá như dynamic, và kết quả sẽ là một dynamic.

Trong trường hợp của bạn, tuyên bố này:

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json); 

Sử dụng năng động, vì vậy, nó getst reated như một biểu hiện năng động. Vì "Gọi phương thức" là một trong các hoạt động của C# bị ràng buộc (7.2), xử lý biên dịch này là động ràng buộc, mà nguyên nhân này để đánh giá để:

dynamic settings = new JavaScriptSerializer().Deserialize<dynamic>(json); 

này, đến lượt nó, làm cho DateTime.Parse biểu phải năng động ràng buộc, do đó làm cho chúng trở dynamic.

"sửa chữa" của bạn hoạt động khi bạn làm DateTime startDate = DateTime.Parse(settings.startDate); vì lực lượng này một chuyển đổi động ẩn (được mô tả trong phần 6.1.8 của spec) về kết quả của phương pháp DateTime.Parse đến một DateTime:

Một chuyển đổi động tiềm ẩn tồn tại từ một biểu thức của kiểu động với bất kỳ loại T. Chuyển đổi được tự động ràng buộc (§7.2.2), có nghĩa là một chuyển đổi tiềm ẩn sẽ được tìm kiếm tại thời gian chạy từ kiểu thời gian chạy của biểu thức T. Nếu không tìm thấy chuyển đổi, ngoại lệ thời gian chạy được ném.

Trong trường hợp này, chuyển đổi hợp lệ, vì vậy, bạn chuyển tất cả mọi thứ trở lại thành ràng buộc tĩnh từ đó trở đi.

3

Tôi không nghĩ điều này đặc biệt đáng ngạc nhiên.

DateTime.Parse(<dynamic>) sẽ đánh giá thành động.

DateTime startDate = <dynamic> thực hiện chuyển nhượng thời gian chạy từ động sang DateTime.

Bạn vừa kết hợp cả hai.

Trình biên dịch không đoán loại DateTime.Parse(<dynamic>) là bất kỳ thứ gì ngoài năng động, nhưng đủ thông minh để nhận ra rằng nếu bạn gán giá trị này cho một DateTime thì giả định nó thành công. Ngày giờ.

+2

Với tôi, thực tế đầu tiên của bạn ** là ** cực kỳ đáng ngạc nhiên. Nếu 'DateTime.Parse' trả về thành công, nó ** sẽ ** trả về một' chuỗi'. Phải không ?! – AakashM

+0

Nó sẽ, nhưng tất cả các cuộc gọi năng động hoạt động như thế này. Trong trường hợp của bạn nó đơn giản nhưng trong các phương thức quá tải nó sẽ không thể giải quyết được, vì vậy nó không bao giờ giải quyết được. –

+0

@AakashM Thực tế đầu tiên được giải thích trong 7.2.2 của thông số ngôn ngữ C# - xem câu trả lời của tôi để biết chi tiết. –

2

Đây là thông số kỹ thuật. Xem §7.6.5:

Một gọi thể hiện là tự động bị ràng buộc (§7.2.2) nếu ít nhất một trong những điều sau đây giữ:

• Các tiểu biểu có kiểu thời gian biên dịch- dynamic.

• Ít nhất một đối số của danh sách đối số tùy chọn có loại thời gian biên dịch dynamicbiểu thức chính không có loại đại biểu.

Hãy xem xét kịch bản này:

class Foo { 
    public int M(string s) { return 0; } 
    public string M(int s) { return String.Empty; } 
} 

Foo foo = new Foo(); 
dynamic d = // something dynamic 
var m = foo.M(d); 

gì nên là loại thời gian biên dịch m? Trình biên dịch không thể nói, bởi vì nó sẽ không biết cho đến khi thời gian chạy mà quá tải của Foo.M được gọi. Vì vậy, nó nói m là động.

Bây giờ, bạn có thể nói rằng có thể tìm ra DateTime.Parse chỉ có một quá tải, và thậm chí nếu không, nhưng tất cả các tình trạng quá tải có cùng kiểu trả về trong trường hợp đó ra loại thời gian biên dịch. Đó sẽ là một điểm công bằng, và có lẽ là tốt nhất Eric Lippert. Tôi đã hỏi một câu hỏi riêng để hiểu rõ hơn về điều này: Why does a method invocation expression have type dynamic even when there is only one possible return type?.

1

Có hai khái niệm khác nhau ở đây

  1. động: đó là bất kỳ kiểu đó đã được giải quyết tại thời gian chạy
  2. var: đó là đánh máy tĩnh ngầm rằng được thực hiện tại thời gian biên dịch

Vì vậy, nếu bạn làm

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json); 
var startDate = DateTime.Parse(settings.startDate); 

tại thời gian biên dịch nó được giải quyết thành loại động và trong thời gian chạy nó được giải quyết theo loại cụ thể. Trình biên dịch kiểm tra phần bên phải new JavaScriptSerializer().Deserialize<dynamic>(json);, trả về động. Tại thời gian biên dịch, điều này hướng dẫn trình biên dịch để thả tất cả các loại kiểm tra an toàn và sống nó cho đến khi thời gian chạy.

Mã này

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json); 
DateTime startDate = DateTime.Parse(settings.startDate); 

dứt khoát nói rằng đối tượng năng động là loại bê tông, do đó trình biên dịch có thể để suy ra các loại, tất cả các phương thức của nó và có thể thực hiện kiểm tra kiểu tĩnh tại thời gian biên dịch.

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