Sự khác biệt giữa ép kiểu và sử dụng phương thức Convert.To ()


90

Tôi có một hàm truyền a doubletrên stringcác giá trị.

string variable = "5.00"; 

double varDouble = (double)variable;

Thay đổi mã đã được kiểm tra và dự án xây dựng với lỗi: System.InvalidCastException: Specified cast is not valid.

Tuy nhiên, sau khi thực hiện những điều sau ...

string variable = "5.00"; 

double varDouble = Convert.ToDouble(variable);

... dự án xây dựng mà không có bất kỳ lỗi.

Sự khác biệt giữa đúc và sử dụng Convert.To()phương pháp? Tại sao ép kiểu ném một Exceptionvà sử dụng Convert.To()không?

c#  casting 


6
Liên quan đến một câu hỏi được tham chiếu , OP hỏi khi nào nên sử dụng ép kiểu hoặc chuyển đổi, và câu trả lời được chấp nhận cho biết, "Đó thực sự là vấn đề lựa chọn cho dù bạn sử dụng cách nào." Tôi đang yêu cầu sự khác biệt giữa diễn viên so với chuyển đổi. Theo ý kiến ​​của tôi, các câu trả lời bên dưới (kudos SO!) Cung cấp chi tiết hơn về sự khác biệt so với "sử dụng cái này hoặc cái kia theo lựa chọn" ... và chi tiết này có thể được sử dụng để đưa ra lựa chọn sáng suốt hơn, về bản chất.

@ edmastermind29 không có nhiều sự khác biệt giữa "sự khác biệt giữa x và y là gì" và "khi nào sử dụng x và y" trong ngữ cảnh lập trình. Cả hai trả lời lẫn nhau.
nawfal

2
Gần 3 năm sau, dường như không có ai trả lời lẫn nhau trong trường hợp này. Q: "Sự khác biệt giữa X và Y là gì?" A: "Nó thực sự là một vấn đề lựa chọn cho dù bạn sử dụng." Không hữu ích lắm.

Dường như không ai có câu trả lời trực tiếp cho câu hỏi nào Hoạt động tốt nhất cũng là một phần của câu hỏi, Từ kinh nghiệm của tôi, tôi thấy Truyền tốt hơn, đặc biệt là trong việc nhận các giá trị cột như thế này .. (int) datatable.Rows [0] [0], nếu chúng tôi biết 100% int của nó
Sundara Prabu

Câu trả lời:


127

Ngay cả khi bạn có thể thấy chúng tương đương bằng cách nào đó thì chúng hoàn toàn khác nhau về mục đích. Đầu tiên chúng ta hãy thử định nghĩa diễn viên là gì:

Truyền là hành động thay đổi một thực thể của một kiểu dữ liệu này thành một thực thể khác.

Nó hơi chung chung và bằng cách nào đó nó tương đương với một chuyển đổi bởi vì một phép ép kiểu thường có cùng một cú pháp của một chuyển đổi, vì vậy câu hỏi nên là khi nào một phép ép kiểu (ẩn hoặc rõ ràng) được cho phép bởi ngôn ngữ và khi nào bạn phải sử dụng ( hơn) chuyển đổi rõ ràng?

Đầu tiên hãy để tôi vẽ một đường đơn giản giữa chúng. Về mặt hình thức (ngay cả khi tương đương với cú pháp ngôn ngữ) một phép ép kiểu sẽ thay đổi kiểu trong khi một chuyển đổi sẽ / có thể thay đổi giá trị (cuối cùng cùng với kiểu). Ngoài ra, một phép truyền có thể hoàn nguyên trong khi một chuyển đổi có thể không.

Chủ đề này khá rộng lớn vì vậy chúng ta hãy cố gắng thu hẹp nó một chút bằng cách loại trừ các toán tử diễn viên tùy chỉnh khỏi trò chơi.

Phôi ngầm

Trong C #, một phép ép kiểu ngầm khi bạn sẽ không mất bất kỳ thông tin nào (xin lưu ý rằng việc kiểm tra này được thực hiện với các loại chứ không phải với các giá trị thực của chúng ).

Các loại nguyên thủy

Ví dụ:

int tinyInteger = 10;
long bigInteger = tinyInteger;

float tinyReal = 10.0f;
double bigReal = tinyReal;

Những loại này là ẩn vì trong quá trình chuyển đổi, bạn sẽ không mất bất kỳ thông tin nào (bạn chỉ làm cho loại rộng hơn). Ngược lại, không được phép truyền ngầm định bởi vì, bất kể giá trị thực của chúng là bao nhiêu (vì chúng chỉ có thể được kiểm tra tại thời điểm chạy), trong quá trình chuyển đổi, bạn có thể mất một số thông tin. Ví dụ: mã này sẽ không biên dịch vì a doublecó thể chứa (và thực tế là có) một giá trị không thể biểu diễn bằng float:

// won't compile!
double bigReal = Double.MaxValue;
float tinyReal = bigReal;

Các đối tượng

Trong trường hợp một đối tượng (một con trỏ tới) thì việc ép kiểu luôn luôn ẩn khi trình biên dịch có thể chắc chắn rằng kiểu nguồn là một lớp dẫn xuất (hoặc nó triển khai) kiểu của lớp đích, ví dụ:

string text = "123";
IFormattable formattable = text;

NotSupportedException derivedException = new NotSupportedException();
Exception baseException = derivedException;

Trong trường hợp này, trình biên dịch biết rằng stringthực thi IFormattablevà đó NotSupportedExceptionlà (dẫn xuất từ) Exceptionvì vậy ép kiểu là ngầm định. Không có thông tin nào bị mất bởi vì các đối tượng không thay đổi kiểu của chúng (điều này khác với kiểu structs và kiểu nguyên thủy vì với kiểu ép kiểu bạn tạo một đối tượng mới thuộc kiểu khác ), cái nhìn của bạn về chúng sẽ thay đổi gì .

Diễn viên rõ ràng

Việc ép kiểu là rõ ràng khi quá trình chuyển đổi không được trình biên dịch thực hiện ngầm và khi đó bạn phải sử dụng toán tử ép kiểu. Thông thường nó có nghĩa là:

  • Bạn có thể mất thông tin hoặc dữ liệu vì vậy bạn phải biết về nó.
  • Việc chuyển đổi có thể không thành công (vì bạn không thể chuyển đổi loại này sang loại khác) vì vậy, một lần nữa, bạn phải biết mình đang làm gì.

Các loại nguyên thủy

Một kiểu truyền rõ ràng là bắt buộc đối với các kiểu nguyên thủy khi trong quá trình chuyển đổi, bạn có thể mất một số dữ liệu, ví dụ:

double precise = Math.Cos(Math.PI * 1.23456) / Math.Sin(1.23456);
float coarse = (float)precise;

float epsilon = (float)Double.Epsilon;

Trong cả hai ví dụ, ngay cả khi các giá trị nằm trong floatphạm vi, bạn sẽ mất thông tin (trong trường hợp này là độ chính xác), vì vậy chuyển đổi phải rõ ràng. Bây giờ hãy thử điều này:

float max = (float)Double.MaxValue;

Việc chuyển đổi này sẽ không thành công, vì vậy, một lần nữa, nó phải rõ ràng để bạn biết về nó và bạn có thể kiểm tra (trong ví dụ, giá trị là không đổi nhưng nó có thể đến từ một số tính toán thời gian chạy hoặc I / O). Quay lại ví dụ của bạn:

// won't compile!
string text = "123";
double value = (double)text;

Điều này sẽ không biên dịch vì trình biên dịch không thể chuyển đổi văn bản thành số. Văn bản có thể chứa bất kỳ ký tự nào, không chỉ số và điều này là quá nhiều, trong C #, ngay cả đối với một kiểu truyền rõ ràng (nhưng nó có thể được cho phép ở ngôn ngữ khác).

Các đối tượng

Chuyển đổi từ con trỏ (thành đối tượng) có thể không thành công nếu các kiểu không liên quan, ví dụ: mã này sẽ không biên dịch (vì trình biên dịch biết không có khả năng chuyển đổi):

// won't compile!    
string text = (string)AppDomain.Current;
Exception exception = (Exception)"abc";

Mã này sẽ biên dịch nhưng nó có thể bị lỗi trong thời gian chạy (nó phụ thuộc vào loại hiệu quả của các đối tượng được truyền) với InvalidCastException:

object obj = GetNextObjectFromInput();
string text = (string)obj;

obj = GetNextObjectFromInput();
Exception exception = (Exception)obj;

Chuyển đổi

Vì vậy, cuối cùng, nếu phôi là chuyển đổi thì tại sao chúng ta cần các lớp như thế Convert? Bỏ qua những khác biệt nhỏ đến từ Convertviệc triển khai và IConvertibletriển khai thực sự bởi vì trong C # với một diễn viên bạn nói với trình biên dịch:

tin tôi đi, loại này là loại đó ngay cả khi bạn không thể biết nó bây giờ, hãy để tôi làm và bạn sẽ thấy.

-hoặc là-

đừng lo lắng, tôi không quan tâm nếu cái gì đó sẽ bị mất trong chuyển đổi này.

Đối với bất kỳ điều gì khác, một hoạt động rõ ràng hơn là cần thiết (hãy nghĩ về hàm ý của các phôi dễ dàng , đó là lý do tại sao C ++ giới thiệu cú pháp dài, dài dòng và rõ ràng cho chúng). Điều này có thể liên quan đến một hoạt động phức tạp (đối với string-> doublechuyển đổi sẽ cần phân tích cú pháp). stringVí dụ: chuyển đổi thành luôn có thể thực hiện được (thông qua ToString()phương thức) nhưng nó có thể có ý nghĩa khác với những gì bạn mong đợi, vì vậy nó phải rõ ràng hơn là một diễn viên ( bạn viết nhiều hơn, bạn nghĩ nhiều hơn về những gì bạn đang làm ).

Việc chuyển đổi này có thể được thực hiện bên trong đối tượng (sử dụng các hướng dẫn IL đã biết cho đối tượng đó), sử dụng các toán tử chuyển đổi tùy chỉnh (được định nghĩa trong lớp để ép kiểu) hoặc các cơ chế phức tạp hơn ( TypeConverterví dụ: các hoặc phương thức lớp). Bạn không biết điều gì sẽ xảy ra để làm điều đó nhưng bạn biết rằng nó có thể thất bại (đó là lý do tại sao IMO khi có thể chuyển đổi được kiểm soát nhiều hơn, bạn nên sử dụng nó). Trong trường hợp của bạn, chuyển đổi chỉ đơn giản sẽ phân tích cú pháp stringđể tạo ra double:

double value = Double.Parse(aStringVariable);

Tất nhiên điều này có thể không thành công vì vậy nếu bạn làm điều đó, bạn nên luôn nắm bắt ngoại lệ nó có thể ném ( FormatException). Ở đây lạc đề nhưng khi TryParsecó sẵn thì bạn nên sử dụng nó (vì về mặt ngữ nghĩa bạn nói nó có thể không phải là số và nó thậm chí còn nhanh hơn ... hỏng).

Các chuyển đổi trong .NET có thể đến từ rất nhiều nơi, truyền TypeConverterngầm / rõ ràng với các toán tử chuyển đổi do người dùng xác định, triển khai IConvertiblevà các phương thức phân tích cú pháp (tôi đã quên điều gì đó?). Hãy xem MSDN để biết thêm chi tiết về chúng.

Để kết thúc câu trả lời dài này, chỉ cần vài từ về toán tử chuyển đổi do người dùng xác định. Nó chỉ là đường để cho lập trình viên sử dụng cast để chuyển đổi loại này sang loại khác. Đó là một phương thức bên trong một lớp (lớp sẽ được ép kiểu) có nội dung "này, nếu anh ấy / cô ấy muốn chuyển đổi kiểu này thành kiểu đó thì tôi có thể làm được". Ví dụ:

float? maybe = 10; // Equals to Nullable<float> maybe = 10;
float sure1 = (float)maybe; // With cast
float sure2 = maybe.Value; // Without cast

Trong trường hợp này, nó rõ ràng vì nó có thể không thành công nhưng điều này được để cho việc thực hiện (ngay cả khi có hướng dẫn về điều này). Hãy tưởng tượng bạn viết một lớp chuỗi tùy chỉnh như thế này:

EasyString text = "123"; // Implicit from string
double value = (string)text; // Explicit to double

Trong quá trình triển khai, bạn có thể quyết định "làm cho cuộc sống của lập trình viên dễ dàng hơn" và để hiển thị chuyển đổi này thông qua một diễn viên (hãy nhớ rằng đó chỉ là một phím tắt để viết ít hơn). Một số ngôn ngữ thậm chí có thể cho phép điều này:

double value = "123";

Cho phép chuyển đổi ngầm sang bất kỳ loại nào (kiểm tra sẽ được thực hiện tại thời điểm chạy). Với các tùy chọn thích hợp, điều này có thể được thực hiện, ví dụ, trong VB.NET. Đó chỉ là một triết lý khác.

Tôi có thể làm gì với chúng?

Vì vậy, câu hỏi cuối cùng là khi nào bạn nên sử dụng cái này hay cái khác. Hãy xem khi nào bạn có thể sử dụng một dàn diễn viên rõ ràng:

  • Chuyển đổi giữa các loại cơ sở.
  • Chuyển đổi từ objectbất kỳ loại nào khác (điều này có thể bao gồm cả việc mở hộp).
  • Chuyển đổi từ lớp dẫn xuất sang lớp cơ sở (hoặc sang giao diện được triển khai).
  • Chuyển đổi từ loại này sang loại khác thông qua toán tử chuyển đổi tùy chỉnh.

Chỉ có thể thực hiện chuyển đổi đầu tiên đối với những chuyển đổi Convertkhác mà bạn không có lựa chọn nào khác và bạn cần sử dụng diễn viên rõ ràng.

Hãy xem ngay bây giờ khi bạn có thể sử dụng Convert:

  • Chuyển đổi từ bất kỳ kiểu cơ sở nào sang kiểu cơ sở khác (với một số hạn chế, xem MSDN ).
  • Chuyển đổi từ bất kỳ loại nào triển khai IConvertiblesang bất kỳ loại nào khác (được hỗ trợ).
  • Chuyển đổi từ / sang một bytemảng thành / từ một chuỗi.

Kết luận

IMO Convertnên được sử dụng mỗi khi bạn biết một chuyển đổi có thể không thành công (do định dạng, vì phạm vi hoặc vì nó có thể không được hỗ trợ), ngay cả khi có thể thực hiện chuyển đổi tương tự với một phép truyền (trừ khi có sẵn thứ gì khác). Nó nói rõ ai sẽ đọc mã của bạn mục đích của bạn là gì và nó có thể thất bại (đơn giản hóa việc gỡ lỗi).

Đối với mọi thứ khác, bạn cần sử dụng cast, không có lựa chọn nào khác, nhưng nếu có một phương pháp khác tốt hơn thì tôi khuyên bạn nên sử dụng nó. Trong ví dụ của bạn, một chuyển đổi từ stringthành doublelà một thứ gì đó (đặc biệt là nếu văn bản đến từ người dùng) rất thường xuyên sẽ không thành công, vì vậy bạn nên làm cho nó càng rõ ràng càng tốt (hơn nữa bạn có nhiều quyền kiểm soát hơn đối với nó), chẳng hạn như sử dụng một TryParsephương pháp.

Chỉnh sửa: sự khác biệt giữa chúng là gì?

Theo câu hỏi được cập nhật và giữ nguyên những gì tôi đã viết trước đây (về thời điểm bạn có thể sử dụng cast so với khi bạn có thể / phải sử dụng Convert) thì điểm cuối cùng cần làm rõ là liệu có sự khác biệt giữa chúng hay không (hơn nữa là cách Convertsử dụng IConvertibleIFormattablegiao diện để nó có thể thực hiện các hoạt động không được phép với phôi).

Câu trả lời ngắn gọn là có, họ cư xử khác nhau . Tôi thấy Convertlớp giống như một lớp phương thức trợ giúp nên thường nó cung cấp một số lợi ích hoặc các hành vi hơi khác một chút. Ví dụ:

double real = 1.6;
int castedInteger = (int)real; // 1
int convertedInteger = Convert.ToInt32(real); // 2

Khá khác biệt, phải không? Việc ép kiểu cắt ngắn (đó là điều mà tất cả chúng ta mong đợi) nhưng Convertthực hiện làm tròn đến số nguyên gần nhất (và điều này có thể không xảy ra nếu bạn không biết về nó). Mỗi phương pháp chuyển đổi đều có sự khác biệt nên không thể áp dụng một quy tắc chung và chúng phải được xem xét theo từng trường hợp ... 19 loại cơ sở để chuyển đổi thành mọi loại khác ... danh sách có thể khá dài, tốt hơn nhiều nên tham khảo trường hợp MSDN bởi trường hợp!


Tôi đã thay đổi câu hỏi để hỏi , Difference between casting and using the Convert.To() method. Nếu không, câu trả lời rất toàn diện. (Tôi hy vọng câu hỏi của tôi được mở lại ...)

@ edmastermind29 Tôi đã chỉnh sửa một chút câu hỏi, chủ đề quá dài ngay cả khi cho một câu trả lời dài (hơn 300 chuyển đổi có thể có trong danh sách). Chuyển đổi bổ sung thêm lợi ích (hoặc chỉ là các hành vi không mong muốn?) Không chỉ so với phôi mà còn so với giao diện IConvertible và IFormattable "đơn giản".
Adriano Repetti

Tôi không thích khái niệm mượn từ C rằng doublecác giá trị không đại diện cho các số nguyên nên "có thể chuyển đổi" thành int. Một dàn diễn viên có vẻ như các mô hình phù hợp trong trường hợp ví dụ như ai lấy Int32giá trị từ một double[]nắm giữ một kết hợp của các số thực và Int32giá trị đã được chuyển đổi sang double[một nỗ lực để chuyển đổi một giá trị không thể thực hiện một cách chính xác trong int32sẽ chỉ ra một điều kiện bất ngờ và nên kích hoạt một ngoại lệ], nhưng tôi nghĩ rằng khi một người muốn chuyển đổi mất mát, người ta nên nói cụ thể về hình thức mà người ta muốn.
supercat

1
Một sự khác biệt nữa là từ đối tượng đến các kiểu nguyên thủy. ví dụobject o = 123; var l = Convert.ToInt64(o); var i = (long) (int) o; var f = (long) o // InvalidCastException
yue shi

1
@ rory.ap đó là một điểm quan trọng. Không, về mặt hình thức đó không phải là ép kiểu ( float-> int) mà là ép buộc . Ví dụ như một dàn diễn viên DerivedClass-> BaseClass. Thật khó hiểu bởi vì trong C #, chúng ta sử dụng cùng một từ (và toán tử) cho cả hai nhưng chúng thực sự là những thứ khác biệt. Một định nghĩa chính thức để phân biệt giữa chúng hơi phức tạp hơn những gì tôi đã viết.
Adriano Repetti

12

Truyền là một cách để nói với trình biên dịch, "Tôi biết rằng bạn nghĩ rằng biến này là một Thanh, nhưng tôi tình cờ biết nhiều hơn bạn; đối tượng thực sự là một Foo, vì vậy hãy để tôi coi nó như thể nó là một Foo từ bây giờ." Sau đó, trong thời gian chạy, nếu đối tượng thực sự hóa ra thực sự là Foo thì mã của bạn sẽ hoạt động, nếu hóa ra đối tượng đó không phải là Foo, thì bạn sẽ có một ngoại lệ. (Cụ thể là một System.InvalidCastException.)

Mặt khác, chuyển đổi là một cách để nói, "Nếu bạn cung cấp cho tôi một đối tượng thuộc loại Thanh, tôi có thể tạo một đối tượng Foo hoàn toàn mới đại diện cho những gì có trong đối tượng Thanh đó. Tôi sẽ không thay đổi đối tượng ban đầu, nó đã thắng" Không đối xử với đối tượng ban đầu theo cách khác, nó sẽ tạo ra một cái gì đó mới chỉ dựa trên một số giá trị khác . Về cách nó sẽ làm điều đó, nó có thể là bất cứ thứ gì. Trong trường hợp này, Convert.ToDoublenó sẽ kết thúc cuộc gọiDouble.Parsetrong đó có tất cả các loại logic phức tạp để xác định loại chuỗi nào đại diện cho giá trị số nào. Bạn có thể viết phương pháp chuyển đổi của riêng mình để ánh xạ các chuỗi thành nhân đôi khác nhau (có lẽ để hỗ trợ một số quy ước hoàn toàn khác để hiển thị số, chẳng hạn như số la mã hoặc bất cứ thứ gì). Một chuyển đổi có thể làm bất cứ điều gì, nhưng ý tưởng là bạn không thực sự yêu cầu trình biên dịch làm bất cứ điều gì cho bạn; bạn là người viết mã để xác định cách tạo đối tượng mới vì trình biên dịch, nếu không có sự trợ giúp của bạn, không có cách nào biết cách ánh xạ (ví dụ) từ a stringđến a double.

Vì vậy, khi nào bạn chuyển đổi và khi nào bạn truyền? Trong cả hai trường hợp, chúng ta có một số biến kiểu, giả sử là A, và chúng ta muốn có một biến kiểu B. Nếu đối tượng A của chúng ta thực sự là B, thì chúng ta ép kiểu. Nếu nó không thực sự là điểm B, thì chúng ta cần Chuyển đổi nó và xác định cách chương trình phải lấy điểm B từ điểm A.


Trong một bài đăng của SO, Eric Lippert đã đề cập rằng không có cái gọi là diễn viên ngầm và đó là chuyển đổi ngầm định . Tôi đã sử dụng thay thế cho diễn viên và chuyển đổi. có gì sai khi nói "diễn viên ngầm"? Không phải nếu chuyển đổi là ẩn mà không yêu cầu bất kỳ diễn viên nào, người ta có thể nói nó là một "diễn viên ngầm"?
rahulaga_dev 20/03/18

1
@RahulAgarwal Diễn viên một thao tác trong đó bạn cần chỉ ra rõ ràng rằng một kiểu đã cho là (hoặc có thể được tạo thành) một thể hiện hợp lệ của kiểu khác. Khi tồn tại một chuyển đổi ngầm định, không cần ép kiểu nào để coi kiểu này là kiểu khác. Vì vậy, nói "truyền ngầm" không thực sự có ý nghĩa (ngoại trừ một số trường hợp có khả năng xảy ra như Eric đã đề cập trong đó toán tử ép kiểu được thêm vào mà nhà phát triển không nhập nó, như khi sử dụng a foreach). Ngoài những ngoại lệ đó, phôi theo định nghĩa là rõ ràng.
Servy

5

Từ MSDN:

Chuyển đổi rõ ràng (truyền): Chuyển đổi rõ ràng yêu cầu toán tử truyền. Truyền là bắt buộc khi thông tin có thể bị mất trong chuyển đổi hoặc khi chuyển đổi có thể không thành công vì các lý do khác. Các ví dụ điển hình bao gồm chuyển đổi số thành một kiểu có độ chính xác thấp hơn hoặc phạm vi nhỏ hơn và chuyển đổi một cá thể lớp cơ sở thành một lớp dẫn xuất.

Hãy xem xét ví dụ sau:

double a = 2548.3;
int b;
b = (int)a; //2548 --> information (.3) lost in the conversion

Và ngoài ra:

Truyền là một cách thông báo rõ ràng cho trình biên dịch rằng bạn dự định thực hiện chuyển đổi và bạn biết rằng có thể xảy ra mất dữ liệu.

Bạn có thể sử dụng System.Convertlớp khi bạn muốn chuyển đổi giữa các loại không tương thích . Sự khác biệt chính giữa truyềnchuyển đổibiên dịchthời gian chạy . Các ngoại lệ chuyển đổi kiểu được xuất hiện tại thời điểm chạy , tức là kiểu ép kiểu không thành công trong thời gian chạy sẽ gây ra một lỗi InvalidCastExceptionđược ném ra.


Kết luận: Trong quá trình truyền, bạn đang nói với trình biên dịch đó athực sự là loại bvà nếu có thì dự án sẽ xây dựng mà không có bất kỳ lỗi nào như ví dụ này:

double s = 2;
int a = (int) s;

Nhưng trong chuyển đổi bạn đang nói với trình biên dịch có một cách để tạo ra một đối tượng mới từ aloại b, xin vui lòng làm điều đó và dự án xây dựng mà không cần bất kỳ lỗi nào nhưng như tôi đã nói nếu loại dàn diễn viên thất bại tại thời gian chạy, nó sẽ gây ra một InvalidCastExceptionđể được ném .

Ví dụ: đoạn mã dưới đây không bao giờ được biên dịch vì trình biên dịch phát hiện không thể truyền biểu thức kiểu DateTimethành kiểu int:

DateTime s = DateTime.Now;
int a = (int)(s);

Nhưng cái này được biên dịch thành công:

DateTime s = DateTime.Now;
int a = Convert.ToInt32(s);

Nhưng tại thời điểm chạy, bạn sẽ nhận được InvalidCastExceptionthông báo:

Truyền từ 'DateTime' sang 'Int32' không hợp lệ.


4

Các Convert.Doublephương pháp thực sự chỉ trong nội bộ gọi là Double.Parse(string)phương pháp.

Cả Stringkiểu và kiểu đều không Doublexác định chuyển đổi rõ ràng / ẩn giữa hai kiểu, do đó, truyền sẽ luôn không thành công.

Các Double.Parsephương pháp sẽ xem xét từng nhân vật trong stringvà xây dựng một giá trị số dựa trên các giá trị của các nhân vật trong string. Nếu bất kỳ ký tự nào không hợp lệ, Parsephương thức không thành công (khiến Convert.Doublephương thức cũng không thành công).


1
Và điều này khác với dàn diễn viên rõ ràng như thế nào?

3
Diễn viên rõ ràng không xem kiểu dữ liệu là gì, nó chỉ xem xét các byte. Một ví dụ sẽ là ép kiểu char x = '1' thành một số nguyên, số nguyên sẽ là 49 vì ký tự cahracter '1' là # 49 trong bảng ascii
user1751547 13/03/2013

@ user1751547 Vì vậy, liệu người dùng có Convert.ToDouble()cái nhìn xa hơn byte và xem xét dữ liệu không?

@ user1751547 Tôi nghĩ đó là loại trực giác cần thiết để trả lời đúng câu hỏi này. Chỉ đơn giản nói "nó không được xác định" là một chút tranh cãi.
Ant P

@ edmastermind29 Có, nó sẽ xem xét kiểu đầu vào và nếu là một chuỗi, nó sẽ đi qua từng ký tự, biết rằng nếu giá trị ascii của char là 49, thì đó là ký tự '1' và chuyển đổi nó đúng cách
user1751547

3

Trong ví dụ của bạn, bạn đang cố gắng truyền một chuỗi thành một chuỗi kép (kiểu không tích phân).

Một chuyển đổi rõ ràng là cần thiết để nó hoạt động.

Và tôi phải chỉ ra rằng bạn có thể đã sử dụng Convert.ToDoublethay vì Convert.ToInt64bạn có thể mất các phần phân số của giá trị kép khi bạn chuyển đổi thành int.

nếu biến của bạn có giá trị "5,25" varDouble sẽ là 5,00 (mất 0,25 do Chuyển đổi thành Int64)

Để trả lời câu hỏi của bạn về truyền và chuyển đổi.

Dàn diễn viên của bạn (dàn diễn viên rõ ràng) không đáp ứng các yêu cầu đối với dàn diễn viên rõ ràng. giá trị bạn đang cố gắng ép kiểu với toán tử ép kiểu không hợp lệ (tức là không tích phân).

Truy cập Trang MSDN này để biết các quy tắc truyền / chuyển đổi


@ edmastermind29 Tôi đã cập nhật câu trả lời của mình. tôi hy vọng nó trả lời câu hỏi của bạn.
scartag

Các yêu cầu cho một diễn viên rõ ràng ... liên quan đến câu hỏi của tôi là gì? Nó có liên quan đến giá trị "không tích phân" không?

@ edmastermind29 Có. nếu giá trị bạn đang cố truyền sang kiểu số không phải là kiểu số, thì quá trình truyền không hợp lệ .. cần có một chuyển đổi.
scartag

3

Truyền không liên quan đến bất kỳ chuyển đổi nào, tức là biểu diễn bên trong của một giá trị không bị thay đổi. Thí dụ:

object o = "Hello"; // o is typed as object and contains a string.
string s = (string)o; // This works only if o really contains a string or null.

Bạn có thể chuyển đổi một doublethành stringnhư thế này

double d = 5;
string s = d.ToString(); // -> "5"

// Or by specifying a format
string formatted = d.ToString("N2"); // -> "5.00"

Bạn có thể chuyển đổi a stringthành a doubletheo một số cách (đây chỉ là hai trong số chúng):

string s = "5";
double d = Double.Parse(s); // Throws an exception if s does not contain a valid number

Hoặc cách an toàn

string s = "5";
double d;
if (Double.TryParse(s, out d)) {
    Console.WriteLine("OK. Result = {0}", d);
} else {
    Console.WriteLine("oops!");
}

Convert.ToDouble()cuộc gọi nội bộDouble.Parse() . Việc sử dụng Convert.ToDouble()hơn Double.Parse()hay không có lợi cho tôi và tại sao?

Convert.ToDoublecó rất nhiều quá tải chấp nhận các loại đầu vào khác nhau. Quá tải chấp nhận stringtrả về 0.0nếu một nullchuỗi được truyền. Ngoài điều này, tôi thấy không có khó khăn trong việc sử dụng nó.
Olivier Jacot-Descombes

Vì vậy, hoặc hoặc ... hoặc Double.Parse()có điều gì đó để cung cấp mà tôi nên xem xét?

Double.Parse()trực tiếp hơn Convert.ToDouble(). Nếu bạn chắc chắn rằng chuỗi của bạn sẽ chứa một số hợp lệ, bạn có thể yên tâm sử dụng nó, nếu không, tôi khuyên bạn nên sử dụng Double.TryParse.
Olivier Jacot-Descombes

1
string variable = "5.00";     
double varDouble = (double)variable;

Chuyển đổi trên chỉ đơn giản là không được phép bởi ngôn ngữ. Đây là danh sách các phôi rõ ràng cho các kiểu số: http://msdn.microsoft.com/en-us/library/yht2cx7b.aspx Như bạn có thể thấy, thậm chí không phải mọi kiểu số đều có thể được chuyển đổi sang một kiểu số khác

Một số thông tin khác về truyền tại đây

Và điều này khác với Convert.ToDouble () như thế nào?

Khi bạn ép kiểu, cấu trúc dữ liệu không bị thay đổi. Vâng, trong trường hợp chuyển đổi giá trị số, bạn có thể mất một vài bit hoặc nhận thêm một vài bit 0. Nhưng bạn vẫn đang làm việc với một số. Bạn chỉ đang thay đổi một lượng bộ nhớ được chiếm bởi số đó. Điều đó đủ an toàn để trình biên dịch làm mọi thứ cần thiết.

Nhưng khi bạn đang cố gắng truyền chuỗi thành một số, bạn không thể làm điều đó vì nó không đủ để thay đổi lượng bộ nhớ được sử dụng bởi biến. Ví dụ: 5,00như một chuỗi là một chuỗi "số": 53 (5) 46 (.) 48 (0) 48 (0) - đó là cho ASCII, nhưng chuỗi sẽ chứa một cái gì đó tương tự. Nếu trình biên dịch sẽ chỉ lấy N byte đầu tiên (4 cho byte kép? Không chắc chắn) từ một chuỗi - đoạn đó sẽ chứa số kép hoàn toàn khác nhau. Đồng thời, Convert.ToDouble () chạy thuật toán đặc biệt sẽ lấy từng ký hiệu của một chuỗi, tìm ra chữ số mà nó đại diện và tạo một số kép cho bạn, nếu chuỗi đại diện cho một số. Nói một cách đại khái, các ngôn ngữ như PHP sẽ gọi Convert.ToDouble cho bạn trong nền. Nhưng C #, giống như một ngôn ngữ được nhập tĩnh, sẽ không làm điều đó cho bạn. Điều này cho phép bạn chắc chắn rằng bất kỳ thao tác nào là an toàn và bạn sẽ không gặp phải điều gì đó không mong muốn khi làm như:

double d = (double)"zzzz"

@ edmastermind29 xem câu trả lời cập nhật của tôi. Tôi đã cố gắng giải thích nó. Giải thích còn lâu mới hoàn hảo, nhưng giả sử nó giải thích sự khác biệt.
Viktor S.

1

Truyền một chuỗi thành một chuỗi kép như vậy không được phép C #, đó là lý do tại sao bạn nhận được Ngoại lệ, bạn cần phải chuyển đổi chuỗi ( tài liệu MSDN hiển thị các đường dẫn chuyển đổi được chấp nhận). Điều này chỉ đơn giản là vì một chuỗi không nhất thiết phải chứa dữ liệu số, nhưng các kiểu số khác nhau sẽ (ngăn các giá trị null). A Convertsẽ chạy một phương thức sẽ kiểm tra chuỗi để xem nó có thể chuyển thành giá trị số hay không. Nếu có thể, thì nó sẽ trả về giá trị đó. Nếu không thể, nó sẽ ném ra một ngoại lệ.

Để chuyển đổi nó, bạn có một số tùy chọn. Bạn đã sử dụng Convertphương pháp trong câu hỏi của mình, phương pháp Parsenày phần lớn tương tự như Convert, nhưng bạn cũng nên xem xét TryParse sẽ cho phép bạn thực hiện:

string variable = "5.00"; 

double varDouble;

if (Double.TryParse(variable, out varDouble)) {
    //Code that runs if the conversion succeeded.
} else {
    //Code that runs if the conversion failed.
}

Điều này tránh trường hợp ngoại lệ có thể xảy ra nếu bạn cố gắng Converthoặc Parsemột chuỗi không phải số.


Việc sử dụng TryParseover ConvertTryParsekiểm tra xem chuyển đổi có thành công không?

@ edmastermind29 Tôi nghĩ vậy. Chuyển đổi sẽ ném ra một ngoại lệ nếu chuyển đổi không thành công. TryParse sẽ trả về boolean, True nếu chuyển đổi thành công và False nếu không thành công.
Keen

1

double varDouble = (double)variablegiả sử rằng đó variableđã là một đôi. Nếu variablekhông phải là một đôi (đó là một chuỗi) thì điều này sẽ không thành công. double varDouble = Convert.ToDouble(variable)như nó nói - nó chuyển đổi. Nếu nó có thể phân tích cú pháp hoặc trích xuất một đoạn kép từ variableđó nó sẽ làm được.

Tôi thứ hai sử dụng Double.Parsehoặc Double.TryParsevì nó chỉ ra rõ ràng hơn những gì được cho là đang xảy ra. Bạn đang bắt đầu với một chuỗi và mong đợi nó có thể chuyển đổi thành một chuỗi kép. Nếu có bất kỳ nghi ngờ nào, hãy sử dụng TryParse.

Nếu variablelà một đối số phương thức, hãy thay đổi kiểu thành double. Làm cho người gọi có trách nhiệm cung cấp đúng loại. Bằng cách đó, trình biên dịch thực hiện công việc cho bạn.


-1

Sự khác biệt quan trọng nhất là nếu sử dụng kiểu ép kiểu và chuyển đổi không thành công (giả sử chúng ta đang chuyển đổi một giá trị float rất lớn thành int) thì sẽ không có ngoại lệ nào được đưa ra và giá trị tối thiểu mà int có thể giữ sẽ được hiển thị. Nhưng trong trường hợp sử dụng Convert , một ngoại lệ sẽ được đưa ra cho các trường hợp như vậy.

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.