ความแตกต่างระหว่างการหล่อและการใช้เมธอด Convert To ()


91

ฉันมีฟังก์ชั่นที่ร่าย a doubleon stringค่า

string variable = "5.00"; 

double varDouble = (double)variable;

มีการตรวจสอบการเปลี่ยนแปลงรหัสและโครงการสร้างขึ้นโดยมีข้อผิดพลาด: System.InvalidCastException: Specified cast is not valid.

อย่างไรก็ตามหลังจากทำสิ่งต่อไปนี้ ...

string variable = "5.00"; 

double varDouble = Convert.ToDouble(variable);

... โครงการสร้างโดยไม่มีข้อผิดพลาดใด ๆ

ความแตกต่างระหว่างการหล่อและการใช้Convert.To()วิธีคืออะไร? เหตุใดการหล่อจึงโยนExceptionและใช้Convert.To()ไม่?

c#  casting 


6
สำหรับคำถามที่อ้างถึง OP จะถามว่าเมื่อใดควรใช้นักแสดงหรือผู้แปลงและคำตอบที่ได้รับการยอมรับจะระบุว่า "มันเป็นเรื่องของทางเลือกที่คุณจะใช้" ฉันกำลังขอความแตกต่างระหว่างนักแสดงกับผู้ที่เปลี่ยนใจเลื่อมใส ในความคิดของฉันคำตอบด้านล่าง (kudos SO!) ให้รายละเอียดเพิ่มเติมเกี่ยวกับความแตกต่างกับ "การใช้สิ่งนี้หรือสิ่งนั้นโดยการเลือก" ... และรายละเอียดนี้สามารถนำไปใช้เพื่อตัดสินใจเลือกอย่างมีข้อมูลมากขึ้น

@ edmastermind29 ไม่มีความแตกต่างกันมากนักระหว่าง "ความแตกต่างระหว่าง x กับ y" และ "เมื่อใดควรใช้ x และ y" ในบริบทการเขียนโปรแกรม ทั้งสองตอบร่วมกัน
nawfal

2
เกือบ 3 ปีต่อมาไม่ปรากฏว่ามีคนตอบคำถามร่วมกันในกรณีนี้ ถาม: "X และ Y ต่างกันอย่างไร" ตอบ: "มันเป็นเรื่องของทางเลือกที่คุณจะใช้" ไม่ค่อยมีประโยชน์.

ดูเหมือนจะไม่มีคำตอบโดยตรงว่าสิ่งใดดีที่สุดก็เป็นส่วนหนึ่งของคำถามเช่นกันจากประสบการณ์ของฉันฉันเห็นว่า Cast ดีกว่าโดยเฉพาะอย่างยิ่งในการรับค่าคอลัมน์เช่นนี้ .. (int) datatable.Rows [0] [0] หาก เรารู้ว่ามันเป็น int 100%
Sundara Prabu

คำตอบ:


127

แม้ว่าคุณจะเห็นว่ามันเทียบเท่ากันก็ตาม แต่ก็มีจุดประสงค์ที่แตกต่างกันอย่างสิ้นเชิง ก่อนอื่นเรามาลองกำหนดว่านักแสดงคืออะไร:

การแคสต์เป็นการกระทำของการเปลี่ยนเอนทิตีของข้อมูลประเภทหนึ่งไปเป็นอีกประเภทหนึ่ง

มันค่อนข้างทั่วไปเล็กน้อยและเทียบเท่ากับการแปลงเนื่องจากนักแสดงมักจะมีไวยากรณ์ของการแปลงเหมือนกันดังนั้นคำถามควรเป็นเมื่อภาษาอนุญาตให้มีการแคสต์ (โดยนัยหรืออย่างชัดเจน) และเมื่อใดที่คุณต้องใช้ ( เพิ่มเติม) การแปลงอย่างชัดเจน?

ก่อนอื่นขอผมลากเส้นง่ายๆระหว่างพวกเขา อย่างเป็นทางการ (แม้ว่าจะเทียบเท่ากับไวยากรณ์ของภาษาก็ตาม) การแคสต์จะเปลี่ยนประเภทในขณะที่การแปลงจะ / อาจเปลี่ยนค่า (ในที่สุดก็รวมกับประเภท) นอกจากนี้การร่ายยังสามารถย้อนกลับได้ในขณะที่การแปลงอาจไม่เป็นเช่นนั้น

หัวข้อนี้ค่อนข้างกว้างดังนั้นลอง จำกัด มันให้แคบลงเล็กน้อยโดยการยกเว้นตัวดำเนินการแคสต์ที่กำหนดเองจากเกม

การร่ายโดยปริยาย

ใน C # การแคสต์มีความหมายโดยนัยเมื่อคุณจะไม่สูญเสียข้อมูลใด ๆ (โปรดทราบว่าการตรวจสอบนี้ดำเนินการกับประเภทไม่ใช่ค่าจริง )

ประเภทดั้งเดิม

ตัวอย่างเช่น:

int tinyInteger = 10;
long bigInteger = tinyInteger;

float tinyReal = 10.0f;
double bigReal = tinyReal;

การแคสต์เหล่านี้มีความหมายโดยนัยเนื่องจากในระหว่างการแปลงคุณจะไม่สูญเสียข้อมูลใด ๆ (คุณแค่ทำให้ประเภทกว้างขึ้น) ในทางกลับกันการแคสต์โดยนัยไม่ได้รับอนุญาตเนื่องจากไม่ว่าค่าจริงจะเป็นเท่าใด (เนื่องจากสามารถตรวจสอบได้เฉพาะในขณะทำงานเท่านั้น) ในระหว่างการแปลงคุณอาจสูญเสียข้อมูลบางอย่าง ตัวอย่างเช่นรหัสนี้จะไม่รวบรวมเนื่องจาก a doubleอาจมี (และเป็นจริง) ค่าที่ไม่สามารถแทนได้ด้วยfloat:

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

วัตถุ

ในกรณีของออบเจ็กต์ (ตัวชี้ไปที่) การส่งจะมีนัยเสมอเมื่อคอมไพเลอร์มั่นใจได้ว่าประเภทซอร์สเป็นคลาสที่ได้รับ (หรือใช้) ประเภทของคลาสเป้าหมายตัวอย่างเช่น:

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

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

ในกรณีนี้คอมไพเลอร์รู้ว่าstringimplements IFormattableและนั่นNotSupportedExceptionคือ (มาจาก) Exceptionดังนั้น cast จึงเป็นนัย ไม่มีข้อมูลสูญหายเนื่องจากออบเจ็กต์ไม่ได้เปลี่ยนประเภท (ซึ่งแตกต่างกับประเภทstructs และ primitive เนื่องจากการแคสต์คุณจะสร้างวัตถุใหม่ประเภทอื่น ) สิ่งที่คุณเปลี่ยนไปคือมุมมองของคุณ

แคสต์ที่ชัดเจน

การแคสต์นั้นชัดเจนเมื่อคอมไพเลอร์ไม่ได้ทำการแปลงโดยปริยายจากนั้นคุณต้องใช้ตัวดำเนินการแคสต์ โดยปกติจะหมายความว่า:

  • คุณอาจสูญเสียข้อมูลหรือข้อมูลดังนั้นคุณต้องระวัง
  • การแปลงอาจล้มเหลว (เนื่องจากคุณไม่สามารถแปลงประเภทหนึ่งเป็นประเภทอื่นได้) ดังนั้นคุณต้องตระหนักถึงสิ่งที่คุณกำลังทำอยู่

ประเภทดั้งเดิม

จำเป็นต้องมีการแคสต์อย่างชัดเจนสำหรับประเภทดั้งเดิมเมื่อในระหว่างการแปลงคุณอาจสูญเสียข้อมูลบางส่วนเช่น:

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

float epsilon = (float)Double.Epsilon;

ในทั้งสองตัวอย่างแม้ว่าค่าจะอยู่ในfloatช่วงดังกล่าว แต่คุณจะสูญเสียข้อมูล (ในกรณีนี้คือความแม่นยำ) ดังนั้นการแปลงจึงต้องชัดเจน ลองทำสิ่งนี้:

float max = (float)Double.MaxValue;

การแปลงนี้จะล้มเหลวอีกครั้งต้องมีความชัดเจนเพื่อให้คุณทราบและคุณอาจตรวจสอบได้ (ในตัวอย่างค่าคงที่ แต่อาจมาจากการคำนวณรันไทม์หรือ I / O) กลับไปที่ตัวอย่างของคุณ:

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

ไม่สามารถคอมไพล์ได้เนื่องจากคอมไพเลอร์ไม่สามารถแปลงข้อความเป็นตัวเลขได้ ข้อความอาจมีอักขระใด ๆ ไม่ใช่ตัวเลขเท่านั้นและมีจำนวนมากเกินไปใน C # แม้กระทั่งสำหรับการแคสต์ที่ชัดเจน (แต่อาจได้รับอนุญาตในภาษาอื่น)

วัตถุ

การแปลงจากพอยน์เตอร์ (ไปยังอ็อบเจ็กต์) อาจล้มเหลวหากประเภทไม่เกี่ยวข้องกันเช่นโค้ดนี้จะไม่คอมไพล์ (เนื่องจากคอมไพเลอร์รู้ว่าไม่มีการแปลงที่เป็นไปได้):

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

รหัสนี้จะคอมไพล์ แต่อาจล้มเหลวในขณะรันไทม์ (ขึ้นอยู่กับประเภทของออบเจ็กต์ที่มีประสิทธิภาพ) ด้วยInvalidCastException:

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

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

Conversion

ดังนั้นในที่สุดถ้าการร่ายเป็นการแปลงแล้วทำไมเราถึงต้องการคลาสเช่นนี้Convert? มองข้ามความแตกต่างเล็กน้อยที่มาจากConvertการนำไปใช้งานและIConvertibleการนำไปใช้จริงเพราะใน C # ที่มีการแคสต์ที่คุณพูดกับคอมไพเลอร์:

เชื่อฉันเถอะประเภทนี้เป็นประเภทที่แม้ว่าคุณจะไม่สามารถรู้ได้ในตอนนี้ให้ฉันทำแล้วคุณจะเห็น

-หรือ-

ไม่ต้องกังวลฉันไม่สนใจว่าบางสิ่งจะหายไปในการแปลงครั้งนี้

สำหรับสิ่งอื่นจำเป็นต้องมีการดำเนินการที่ชัดเจนมากขึ้น (ลองนึกถึงผลกระทบของการร่ายแบบง่ายนั่นคือเหตุผลที่ C ++ แนะนำไวยากรณ์ที่ยาวละเอียดและชัดเจนสำหรับพวกเขา) ซึ่งอาจเกี่ยวข้องกับการดำเนินการที่ซับซ้อน (สำหรับstring-> doubleการแปลงจะต้องมีการแยกวิเคราะห์) stringตัวอย่างเช่นการแปลงเป็นเป็นไปได้เสมอ (ผ่านToString()วิธีการ) แต่อาจหมายถึงสิ่งที่แตกต่างจากที่คุณคาดหวังดังนั้นจึงต้องมีความชัดเจนมากกว่าการแคสต์ ( คุณเขียนมากขึ้นคุณคิดเกี่ยวกับสิ่งที่คุณทำมากขึ้น)

การแปลงนี้สามารถทำได้ภายในออบเจ็กต์ (โดยใช้คำสั่ง IL ที่รู้จักสำหรับสิ่งนั้น) โดยใช้ตัวดำเนินการการแปลงแบบกำหนดเอง (กำหนดไว้ในคลาสที่จะส่ง) หรือกลไกที่ซับซ้อนมากขึ้น ( TypeConverters หรือ class method เป็นต้น) คุณไม่ทราบว่าจะเกิดอะไรขึ้นกับสิ่งนั้น แต่คุณทราบดีว่าอาจล้มเหลว (นั่นคือเหตุผลที่ IMO เมื่อมีการแปลงที่ควบคุมได้มากขึ้นคุณควรใช้) ในกรณีของคุณการแปลงจะแยกวิเคราะห์stringเพื่อสร้างdouble:

double value = Double.Parse(aStringVariable);

แน่นอนว่าสิ่งนี้อาจล้มเหลวดังนั้นหากคุณทำเช่นนั้นคุณควรจับข้อยกเว้นที่อาจโยน ( FormatException) มันไม่อยู่ในหัวข้อที่นี่ แต่เมื่อมีTryParseให้ใช้งานคุณควรใช้มัน (เพราะคุณพูดอย่างมีความหมายมันอาจไม่ใช่ตัวเลขและมันเร็วกว่า ... ที่จะล้มเหลว)

การแปลงใน. NET อาจมาจากหลาย ๆ ที่การแคสTypeConverterต์โดยนัย / ชัดแจ้งด้วยตัวดำเนินการ Conversion ที่ผู้ใช้กำหนดการใช้งานIConvertibleและวิธีการแยกวิเคราะห์ (ฉันลืมอะไรไปหรือเปล่า) ดู MSDN สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับพวกเขา

เพื่อจบคำตอบยาว ๆ นี้เพียงไม่กี่คำเกี่ยวกับตัวดำเนินการแปลงที่ผู้ใช้กำหนด เป็นเพียงน้ำตาลที่จะให้โปรแกรมเมอร์ใช้ cast เพื่อแปลงประเภทหนึ่งเป็นอีกประเภทหนึ่ง เป็นวิธีการภายในคลาส (วิธีที่จะถูกแคสต์) ที่ระบุว่า "เฮ้ถ้าเขา / เธอต้องการแปลงประเภทนี้เป็นประเภทนั้นฉันก็ทำได้" ตัวอย่างเช่น:

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

ในกรณีนี้เป็นเรื่องที่ชัดเจนเนื่องจากอาจล้มเหลว แต่จะปล่อยให้นำไปใช้งาน (แม้ว่าจะมีแนวทางเกี่ยวกับเรื่องนี้ก็ตาม) ลองนึกภาพคุณเขียนคลาสสตริงที่กำหนดเองดังนี้:

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

ในการนำไปใช้งานของคุณคุณอาจตัดสินใจที่จะ "ทำให้ชีวิตของโปรแกรมเมอร์ง่ายขึ้น" และเปิดเผย Conversion นี้ผ่านการแคสต์ (โปรดจำไว้ว่าเป็นเพียงทางลัดในการเขียนน้อยลง) บางภาษาอาจอนุญาตสิ่งนี้:

double value = "123";

อนุญาตการแปลงโดยนัยเป็นประเภทใด ๆ (การตรวจสอบจะทำในขณะทำงาน) ด้วยตัวเลือกที่เหมาะสมสามารถทำได้ตัวอย่างเช่นใน VB.NET มันเป็นเพียงปรัชญาที่แตกต่างกัน

ฉันจะทำอะไรกับพวกเขาได้บ้าง?

คำถามสุดท้ายคือเวลาที่คุณควรใช้อย่างใดอย่างหนึ่ง มาดูกันว่าเมื่อใดที่คุณสามารถใช้การร่ายแบบโจ่งแจ้ง:

  • การแปลงระหว่างประเภทพื้นฐาน
  • การแปลงจากobjectเป็นประเภทอื่น ๆ (ซึ่งอาจรวมถึงการแกะกล่องด้วย)
  • การแปลงจากคลาสที่ได้รับมาเป็นคลาสพื้นฐาน (หรือไปยังอินเทอร์เฟซที่ใช้งาน)
  • การแปลงจากประเภทหนึ่งไปยังอีกประเภทหนึ่งผ่านตัวดำเนินการแปลงที่กำหนดเอง

สามารถทำได้เฉพาะการแปลงครั้งแรกเท่านั้น Convertสำหรับคนอื่น ๆ ที่คุณไม่มีทางเลือกและคุณต้องใช้นักแสดงที่ชัดเจน

มาดูกันว่าคุณสามารถใช้Convert:

  • การแปลงจากประเภทฐานใด ๆ เป็นประเภทฐานอื่น (มีข้อ จำกัด บางประการโปรดดูMSDN )
  • การแปลงจากประเภทใดก็ได้ที่นำIConvertibleไปใช้กับประเภทอื่น ๆ (ที่รองรับ)
  • การแปลงจาก / ไปยังbyteอาร์เรย์เป็น / จากสตริง

ข้อสรุป

Convertควรใช้IMO ทุกครั้งที่คุณทราบว่าการแปลงอาจล้มเหลว (เนื่องจากรูปแบบเนื่องจากช่วงหรือเนื่องจากอาจไม่รองรับ) แม้ว่าการแปลงเดียวกันจะทำได้ด้วยการส่งก็ตาม (เว้นแต่จะมีอย่างอื่น) มันทำให้ชัดเจนว่าใครจะอ่านโค้ดของคุณเจตนาของคุณคืออะไรและอาจล้มเหลว (ทำให้การแก้ไขจุดบกพร่องง่ายขึ้น)

สำหรับอย่างอื่นคุณต้องใช้นักแสดงไม่มีทางเลือก แต่ถ้ามีวิธีอื่นที่ดีกว่านี้ฉันขอแนะนำให้คุณใช้มัน ในตัวอย่างของการแปลงจากstringการdoubleเป็นสิ่งที่ (โดยเฉพาะถ้าข้อความมาจากผู้ใช้) มากมักจะล้มเหลวดังนั้นคุณควรทำให้มันชัดเจนมากที่สุดเท่าที่เป็นไปได้ (ยิ่งกว่านั้นคุณจะได้รับการควบคุมที่มากกว่ามัน) ตัวอย่างเช่นการใช้TryParseวิธีการ

แก้ไข: อะไรคือความแตกต่างระหว่างพวกเขา?

ตามคำถามที่อัปเดตและเก็บสิ่งที่ฉันเขียนไว้ก่อนหน้านี้ (เกี่ยวกับเวลาที่คุณสามารถใช้นักแสดงเมื่อเทียบกับเวลาที่คุณทำได้ / ต้องใช้Convert) ประเด็นสุดท้ายที่จะชี้แจงคือมีความแตกต่างระหว่างกันหรือไม่ (ยิ่งไปกว่านั้นการConvertใช้IConvertibleและIFormattableอินเทอร์เฟซเพื่อให้สามารถดำเนินการได้ ไม่อนุญาตให้ใช้กับนักแสดง)

ตอบสั้น ๆ คือใช่พวกเขาทำงานแตกต่างกัน ฉันเห็นConvertคลาสนี้เหมือนคลาสวิธีการช่วยเหลือบ่อยครั้งจึงให้ประโยชน์บางอย่างหรือพฤติกรรมที่แตกต่างกันเล็กน้อย ตัวอย่างเช่น:

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

แตกต่างกันใช่มั้ย? นักแสดงจะตัดทอน (เป็นสิ่งที่เราทุกคนคาดหวัง) แต่จะConvertทำการปัดเศษเป็นจำนวนเต็มใกล้เคียงที่สุด (และอาจไม่เกิดขึ้นหากคุณไม่ทราบ) วิธีการแปลงแต่ละวิธีนำเสนอความแตกต่างดังนั้นจึงไม่สามารถใช้กฎทั่วไปได้และจะต้องเห็นเป็นกรณี ๆ ไป ... 19 ประเภทฐานในการแปลงเป็นประเภทอื่น ๆ ... รายการอาจค่อนข้างยาวและดีกว่ามากในการปรึกษากรณี MSDN โดย กรณี!


ฉันเปลี่ยนคำถามเป็นถามDifference between casting and using the Convert.To() methodว่า มิฉะนั้นคำตอบที่ครอบคลุมมาก (ฉันหวังว่าคำถามของฉันจะเปิดอีกครั้ง ... )

@ edmastermind29 ฉันแก้ไขคำถามเล็กน้อยหัวข้อยาวเกินไปแม้จะเป็นคำตอบที่ยาว (มีรายการ Conversion มากกว่า 300 รายการ) Convert เพิ่มประโยชน์ (หรือพฤติกรรมที่ไม่คาดคิด?) ไม่เพียง แต่เทียบกับ casts เท่านั้น แต่ยังเทียบกับอินเทอร์เฟซ IConvertible และ IFormattable แบบ "ธรรมดา" ด้วย
Adriano Repetti

ฉันไม่ชอบความคิดที่ยืมมาจากซีที่doubleค่าซึ่งไม่ได้เป็นตัวแทนของตัวเลขทั้งหมดควรจะเป็น "แปลงสภาพ" intเพื่อ โยนจะดูเหมือนกระบวนทัศน์ที่เหมาะสมในกรณีที่เช่นหนึ่งจะดึงInt32ค่าจากdouble[]ซึ่งถือส่วนผสมของจำนวนจริงและInt32ค่าที่ได้รับการแปลงเป็นdouble[ความพยายามที่จะแปลงค่าที่ไม่ representable แม่นยำในint32จะแสดงให้เห็นสภาพที่ไม่คาดคิด และควรทำให้เกิดข้อยกเว้น] แต่ฉันคิดว่าเมื่อมีคนต้องการ Conversion ที่สูญเสียควรเจาะจงเกี่ยวกับรูปแบบที่ต้องการ
supercat

1
ความแตกต่างอีกประการหนึ่งคือจากวัตถุไปจนถึงประเภทดั้งเดิม เช่นobject o = 123; var l = Convert.ToInt64(o); var i = (long) (int) o; var f = (long) o // InvalidCastException
yue shi

1
@ rory.ap นั่นคือจุดสำคัญ ไม่มีอย่างเป็นทางการที่ไม่ได้เป็นนักแสดง ( float-> int) แต่การบังคับขู่เข็ญ โยนอาจจะยกตัวอย่างเช่น->DerivedClass BaseClassมันสับสนเพราะใน C # เราใช้คำเดียวกัน (และตัวดำเนินการ) สำหรับทั้งคู่ แต่จริงๆแล้วมันต่างกัน คำจำกัดความที่เป็นทางการเพื่อแยกความแตกต่างนั้นซับซ้อนกว่าที่ฉันเขียนเล็กน้อย
Adriano Repetti

13

การแคสต์เป็นวิธีการบอกคอมไพเลอร์ว่า "ฉันรู้ว่าคุณคิดว่าตัวแปรนี้เป็น Bar แต่ฉันรู้มากกว่าคุณจริงๆแล้ววัตถุนั้นเป็น Foo ดังนั้นขอให้ฉันถือว่ามันเป็น Foo จาก ตอนนี้” จากนั้นในรันไทม์หากวัตถุจริงกลายเป็น Foo จริงๆแล้วรหัสของคุณจะใช้งานได้ถ้าปรากฎว่าวัตถุนั้นไม่ใช่ Foo เลยคุณจะได้รับข้อยกเว้น (โดยเฉพาะกSystem.InvalidCastException.)

ในทางกลับกันการแปลงเป็นวิธีการพูดว่า "ถ้าคุณให้วัตถุประเภท Bar ฉันสามารถสร้างวัตถุ Foo ใหม่เอี่ยมที่แสดงถึงสิ่งที่อยู่ในวัตถุ Bar นั้นได้ฉันจะไม่เปลี่ยนวัตถุดั้งเดิมมันจะชนะ ' t ปฏิบัติต่อวัตถุดั้งเดิมแตกต่างกันมันจะสร้างสิ่งใหม่ที่ขึ้นอยู่กับค่าอื่น ๆ เกี่ยวกับวิธีที่จะทำเช่นนั้นมันอาจเป็นอะไรก็ได้ในกรณีของConvert.ToDoubleมันจะจบลงด้วยการเรียกDouble.Parseซึ่งมีตรรกะที่ซับซ้อนทุกประเภทสำหรับการกำหนดประเภทของสตริงที่แสดงถึงค่าตัวเลข คุณสามารถเขียนวิธีการแปลงของคุณเองที่แมปสตริงให้แตกต่างกันเป็นสองเท่า (อาจจะสนับสนุนรูปแบบการแสดงตัวเลขที่แตกต่างกันโดยสิ้นเชิงเช่นเลขโรมันหรืออะไรก็ตาม) การแปลงสามารถทำอะไรก็ได้ แต่แนวคิดก็คือคุณไม่ได้ขอให้คอมไพเลอร์ทำอะไรให้คุณจริงๆ คุณเป็นคนหนึ่งที่เขียนโค้ดเพื่อกำหนดวิธีการสร้างวัตถุใหม่เนื่องจากคอมไพเลอร์ไม่มีทางรู้วิธีแมป (ตามตัวอย่าง) a stringถึง a double.

คุณเปลี่ยนใจเลื่อมใสเมื่อไหร่และเมื่อไหร่ที่คุณแคสต์ ในทั้งสองกรณีเรามีตัวแปรประเภทหนึ่งสมมติว่า A และเราต้องการมีตัวแปรประเภท B ถ้าวัตถุ A ของเราจริงๆแล้วภายใต้ประทุนคือ B เราก็จะร่าย ถ้ามันไม่ใช่ B จริงๆเราต้องแปลงมันและกำหนดว่าโปรแกรมควรจะได้ B จาก A อย่างไร


ในตอนหนึ่งของSO โพสต์เอริค Lippert บอกว่าไม่มีสิ่งดังกล่าวเรียกว่าหล่อนัย และมันก็เป็นแปลงโดยปริยาย ฉันใช้การร่ายและการแปลงสลับกัน มีอะไรผิดปกติในการพูดว่า "โยนโดยนัย"? หากการแปลงเป็นนัยโดยไม่ต้องใช้นักแสดงคนใดคนหนึ่งสามารถพูดได้ว่าเป็นการ "โยนโดยนัย" หรือไม่?
rahulaga_dev

1
@RahulAgarwal นักแสดงคือการดำเนินการที่คุณต้องระบุอย่างชัดเจนว่าประเภทที่กำหนดคือ (หรือสามารถสร้างเป็น) อินสแตนซ์ที่ถูกต้องของประเภทอื่นได้ เมื่อมีการแปลงโดยนัยไม่จำเป็นต้องใช้นักแสดงเพื่อให้ประเภทเป็นประเภทอื่น ดังนั้นการพูดว่า "ส่งโดยนัย" จึงไม่สมเหตุสมผล (ยกเว้นในบางสถานการณ์เช่นที่เอริคพูดถึงที่มีการเพิ่มตัวดำเนินการแคสต์โดยที่นักพัฒนาไม่ต้องพิมพ์เช่นเมื่อใช้ a foreach) นอกเหนือจากข้อยกเว้นเหล่านั้นการร่ายเป็นไปตามความหมายที่ชัดเจน
Servy

6

จากMSDN:

การแปลงที่ชัดเจน (แคสต์): การแปลงที่ชัดเจนต้องใช้ตัวดำเนินการแคสต์ จำเป็นต้องแคสต์เมื่อข้อมูลอาจสูญหายในการแปลงหรือเมื่อการแปลงอาจไม่สำเร็จด้วยเหตุผลอื่น ๆ ตัวอย่างทั่วไป ได้แก่ การแปลงตัวเลขเป็นประเภทที่มีความแม่นยำน้อยกว่าหรือช่วงที่เล็กกว่าและการแปลงอินสแตนซ์คลาสพื้นฐานเป็นคลาสที่ได้รับ

พิจารณาตัวอย่างต่อไปนี้:

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

และนอกจากนี้ยังมี:

การแคสต์เป็นวิธีการแจ้งคอมไพเลอร์อย่างชัดเจนว่าคุณตั้งใจจะทำการแปลงและคุณทราบว่าข้อมูลอาจสูญหาย

คุณสามารถใช้System.Convertระดับเมื่อคุณต้องการการแปลงระหว่างเข้ากันไม่ได้ประเภท ความแตกต่างหลักระหว่างการหล่อและการแปลงคือคอมไพล์และเวลาทำงาน ข้อยกเว้นการแปลงประเภทจะปรากฏขึ้นในขณะรันไทม์กล่าวคือการแคสต์ประเภทที่ล้มเหลวในขณะรันไทม์จะทำให้เกิดการInvalidCastExceptionโยน


สรุป:ในการแคสต์คุณกำลังบอกกับคอมไพเลอร์ว่าaเป็นประเภทจริง ๆbและหากเป็นเช่นนั้นโปรเจ็กต์จะสร้างโดยไม่มีข้อผิดพลาดดังตัวอย่างนี้:

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

แต่ในการแปลงคุณกำลังพูดกับคอมไพเลอร์มีวิธีสร้างวัตถุใหม่จาก aประเภทbโปรดทำมันและโครงการสร้างไม่มีข้อผิดพลาดใด ๆ แต่ที่ผมกล่าวว่าถ้าโยนประเภทการล้มเหลวที่ใช้เวลาก็จะทำให้เกิดInvalidCastExceptionการ ถูกโยน

ตัวอย่างเช่นโค้ดด้านล่างจะไม่คอมไพล์เนื่องจากคอมไพเลอร์ตรวจพบว่าไม่สามารถแคสต์นิพจน์ประเภทได้ DateTimeจะพิมพ์ได้int:

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

แต่คอมไพล์สำเร็จแล้ว:

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

แต่ในเวลาทำงานคุณจะได้รับInvalidCastExceptionซึ่งระบุว่า:

แคสต์จาก "DateTime" ถึง "Int32" ไม่ถูกต้อง


4

ในตัวอย่างของคุณคุณกำลังพยายามที่จะโยนสตริงเป็นสองเท่า (ไม่ใช่ประเภทอินทิกรัล)

จำเป็นต้องมีการแปลงอย่างชัดเจนเพื่อให้สามารถใช้งานได้

และฉันต้องชี้ว่าคุณสามารถใช้Convert.ToDoubleแทนได้Convert.ToInt64เนื่องจากคุณสามารถสูญเสียส่วนที่เป็นเศษส่วนของค่าสองเท่าเมื่อคุณแปลงเป็น int

หากตัวแปรของคุณมีค่า "5.25" varDouble จะเป็น 5.00 (การสูญเสีย 0.25 เนื่องจากการแปลงเป็น Int64)

เพื่อตอบคำถามของคุณเกี่ยวกับการแคสต์เทียบกับการแปลง

นักแสดงของคุณ (นักแสดงที่ไม่เหมาะสม) ไม่ตรงตามข้อกำหนดสำหรับนักแสดงที่ไม่เหมาะสม ค่าที่คุณพยายามร่ายด้วยตัวดำเนินการแคสนั้นไม่ถูกต้อง (เช่นไม่ใช่อินทิกรัล)

ไปที่หน้า MSDNเพื่อดูกฎการแคสต์ / การแปลง


@ edmastermind29 ฉันอัปเดตคำตอบแล้ว ฉันหวังว่ามันจะตอบคำถามของคุณ
scartag

ข้อกำหนดสำหรับนักแสดงที่ชัดเจน ... ที่เกี่ยวข้องกับคำถามของฉันคืออะไร เกี่ยวข้องกับค่า "non integral" หรือไม่?

@ edmastermind29 ครับ. หากค่าที่คุณพยายามแคสต์เป็นประเภทตัวเลขไม่ใช่ตัวเลขแสดงว่าการแคสต์ไม่ถูกต้องต้องมีการแปลง
scartag

4

Convert.DoubleวิธีจริงเพียงภายในเรียกDouble.Parse(string)วิธีการ

ทั้งStringประเภทหรือDoubleประเภทไม่ได้กำหนดการแปลงอย่างชัดเจน / โดยนัยระหว่างทั้งสองประเภทดังนั้นการแคสต์จะล้มเหลวเสมอ

Double.Parseวิธีการจะดูที่ตัวละครในแต่ละและสร้างค่าตัวเลขตามค่าของตัวละครในนั้นstring stringหากอักขระใด ๆ ไม่ถูกต้องParseเมธอดจะล้มเหลว (ทำให้Convert.Doubleเมธอดล้มเหลวเช่นกัน)


1
และสิ่งนี้แตกต่างจากนักแสดงที่ชัดเจนอย่างไร?

3
การแคสต์ที่ชัดเจนไม่ได้ดูว่าประเภทข้อมูลคืออะไร แต่เพียงแค่ดูที่ไบต์ ตัวอย่างเช่นการแคสต์ char x = '1' เป็นจำนวนเต็มจำนวนเต็มจะเป็น 49 เนื่องจาก cahracter '1' คือ # 49 ในตาราง ascii
user1751547

@ user1751547 ดังนั้นผู้ใช้ไฟล์ Convert.ToDouble()มองข้ามไบต์และพิจารณาข้อมูลหรือไม่?

@ user1751547 ฉันคิดว่านั่นเป็นสัญชาตญาณที่จำเป็นในการตอบคำถามนี้อย่างถูกต้อง เพียงแค่พูดว่า "ไม่ได้กำหนด" ก็เป็นการสงสัยเล็กน้อย
Ant P

@ edmastermind29 ใช่มันจะดูประเภทอินพุตและถ้าเป็นสตริงมันจะผ่านแต่ละอักขระโดยรู้ว่าถ้าค่า ascii ของ char คือ 49 แสดงว่าเป็นอักขระ '1' และแปลงให้ถูกต้อง
user1751547

3

การแคสต์ไม่เกี่ยวข้องกับการแปลงใด ๆ กล่าวคือการแทนค่าภายในจะไม่มีการเปลี่ยนแปลง ตัวอย่าง:

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.

คุณสามารถแปลงdoubleไปstringเช่นนี้

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

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

คุณสามารถแปลง a เป็นstringa ได้doubleหลายวิธี (ที่นี่มีเพียงสองวิธีเท่านั้น):

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

หรือวิธีที่ปลอดภัย

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

Convert.ToDouble()Double.Parse()สายภายใน เป็นประโยชน์ของฉันที่จะใช้Convert.ToDouble()มากกว่าDouble.Parse()หรือไม่และทำไม?

Convert.ToDoubleมีโอเวอร์โหลดจำนวนมากที่ยอมรับอินพุตประเภทต่างๆ โอเวอร์โหลดที่ยอมรับstringผลตอบแทน0.0หากมีการส่งnullสตริง นอกเหนือจากนี้ฉันไม่เห็นความสำคัญในการใช้งาน
Olivier Jacot-Descombes

อย่างใดอย่างหนึ่งหรือ ... หรือDouble.Parse()มีสิ่งที่จะนำเสนอที่ฉันควรพิจารณา?

Double.Parse()Convert.ToDouble()โดยตรงมากกว่า Double.TryParseถ้าคุณแน่ใจว่าสายของคุณจะมีตัวเลขที่ถูกต้องคุณสามารถใช้มันเป็นอย่างอื่นผมแนะนำให้คุณใช้
Olivier Jacot-Descombes

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

การแปลงข้างต้นไม่ได้รับอนุญาตจากภาษา นี่คือรายการของการร่ายอย่างชัดเจนสำหรับประเภทตัวเลข: http://msdn.microsoft.com/en-us/library/yht2cx7b.aspxอย่างที่คุณเห็นแม้ไม่ใช่ทุกประเภทที่เป็นตัวเลขก็สามารถแปลงเป็นประเภทตัวเลขอื่นได้

ข้อมูลเพิ่มเติมเกี่ยวกับการแคสต์ที่นี่

และสิ่งนี้แตกต่างกับ Convert ToDouble () อย่างไร?

เมื่อคุณแคสต์ประเภทโครงสร้างข้อมูลจะไม่เปลี่ยนแปลง ในกรณีของการแปลงค่าตัวเลขคุณอาจหลวมไม่กี่บิตหรือได้รับ 0 บิตเพิ่มเติมเล็กน้อย แต่คุณยังคงทำงานกับหมายเลข คุณแค่เปลี่ยนจำนวนหน่วยความจำที่ได้มาจากตัวเลขนั้น ที่ปลอดภัยเพียงพอสำหรับคอมไพเลอร์ทำทุกอย่างที่จำเป็น

แต่เมื่อคุณพยายามแคสต์สตริงเป็นตัวเลขคุณไม่สามารถทำได้เนื่องจากไม่เพียงพอที่จะเปลี่ยนจำนวนหน่วยความจำที่ใช้โดยตัวแปร ตัวอย่างเช่น 5.00เนื่องจากสตริงเป็นลำดับของ "ตัวเลข": 53 (5) 46 (.) 48 (0) 48 (0) - สำหรับ ASCII แต่สตริงจะมีสิ่งที่คล้ายกัน หากคอมไพเลอร์จะใช้ N (4 เป็นคู่หรือไม่แน่ใจ) ไบต์แรกจากสตริง - ชิ้นส่วนนั้นจะมีเลขคู่ที่ต่างกันโดยสิ้นเชิง ในขณะเดียวกัน Convert ToDouble () ให้เรียกใช้อัลกอริทึมพิเศษซึ่งจะใช้สัญลักษณ์ของสตริงแต่ละตัวหาตัวเลขที่แสดงถึงและสร้างตัวเลขสองเท่าให้คุณหากสตริงแสดงถึงตัวเลข ภาษาเช่น PHP จะพูดประมาณว่า Convert ToDouble สำหรับคุณในพื้นหลัง แต่ C # เช่นเดียวกับภาษาที่พิมพ์แบบคงที่จะไม่ทำเช่นนั้นสำหรับคุณ สิ่งนี้ช่วยให้คุณมั่นใจได้ว่าการดำเนินการใด ๆ เป็นประเภทที่ปลอดภัยและคุณจะไม่ได้รับสิ่งที่ไม่คาดคิดเช่น:

double d = (double)"zzzz"

@ edmastermind29 ดูคำตอบที่อัปเดตของฉัน ฉันพยายามอธิบายแล้ว คำอธิบายยังห่างไกลจากความสมบูรณ์แบบ แต่สมมติว่าอธิบายความแตกต่าง
Viktor S.

1

การแคสต์สตริงเป็นสองเท่านั้นไม่ได้รับอนุญาต C # ซึ่งเป็นสาเหตุที่คุณได้รับ Exception คุณต้องมีการแปลงสตริง ( เอกสาร MSDNที่แสดงเส้นทาง Conversion ที่ยอมรับได้) นี่เป็นเพียงเพราะสตริงไม่จำเป็นต้องมีข้อมูลตัวเลข แต่ประเภทตัวเลขต่างๆจะ (จำกัด ค่า null) A Convertจะเรียกใช้เมธอดที่จะตรวจสอบสตริงเพื่อดูว่าสามารถเปลี่ยนเป็นค่าตัวเลขได้หรือไม่ ถ้าทำได้ก็จะคืนค่านั้น หากไม่สามารถทำได้ก็จะทำให้เกิดข้อยกเว้น

คุณมีหลายทางเลือกในการแปลง คุณใช้ConvertวิธีการในคำถามของคุณParseซึ่งส่วนใหญ่คล้ายกับConvertแต่คุณควรดูTryParseซึ่งจะช่วยให้คุณทำ

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.
}

สิ่งนี้หลีกเลี่ยงข้อยกเว้นที่เป็นไปได้หากคุณพยายามConvertหรือParseสตริงที่ไม่ใช่ตัวเลข


เป็นข้อดีของฉันที่จะใช้TryParseover ConvertเพราะTryParseตรวจสอบว่าการแปลงสำเร็จหรือไม่

@ edmastermind29 ฉันคิดอย่างนั้น การแปลงจะทำให้เกิดข้อยกเว้นหากการแปลงล้มเหลว TryParse จะส่งคืนบูลีน True หากการแปลงสำเร็จและ False หากล้มเหลว
Keen

1

double varDouble = (double)variableถือว่าvariableเป็นสองเท่าแล้ว ถ้าvariableไม่ใช่คู่ (เป็นสตริง) ก็จะล้มเหลว double varDouble = Convert.ToDouble(variable)ไม่เหมือนที่พูด - มันแปลง หากสามารถแยกวิเคราะห์หรือแยกคู่จากvariableนั้นก็จะ

ฉันใช้ครั้งที่สองDouble.ParseหรือDouble.TryParseเพราะมันบ่งบอกได้ชัดเจนมากขึ้นว่าอะไรจะเกิดขึ้น คุณกำลังเริ่มต้นด้วยสตริงและคาดหวังว่ามันจะเปลี่ยนเป็นคู่ได้ หากมีข้อสงสัยให้ใช้TryParse.

ถ้าvariableเป็นอาร์กิวเมนต์เมธอดให้เปลี่ยนประเภทเป็นสองเท่า ให้ผู้โทรรับผิดชอบในการจัดหาประเภทที่ถูกต้อง วิธีนี้คอมไพเลอร์จะทำงานให้คุณ


-1

ความแตกต่างที่สำคัญที่สุดคือหากใช้การแคสต์ประเภทและการแปลงล้มเหลว (กล่าวว่าเรากำลังแปลงค่า float ที่ใหญ่มากเป็น int) จะไม่มีการโยนข้อยกเว้นและค่าต่ำสุดที่ int สามารถระงับได้จะแสดงขึ้น แต่ในกรณีที่ใช้Convert จะมีข้อยกเว้นสำหรับสถานการณ์ดังกล่าว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.