แม้ว่าคุณจะเห็นว่ามันเทียบเท่ากันก็ตาม แต่ก็มีจุดประสงค์ที่แตกต่างกันอย่างสิ้นเชิง ก่อนอื่นเรามาลองกำหนดว่านักแสดงคืออะไร:
การแคสต์เป็นการกระทำของการเปลี่ยนเอนทิตีของข้อมูลประเภทหนึ่งไปเป็นอีกประเภทหนึ่ง
มันค่อนข้างทั่วไปเล็กน้อยและเทียบเท่ากับการแปลงเนื่องจากนักแสดงมักจะมีไวยากรณ์ของการแปลงเหมือนกันดังนั้นคำถามควรเป็นเมื่อภาษาอนุญาตให้มีการแคสต์ (โดยนัยหรืออย่างชัดเจน) และเมื่อใดที่คุณต้องใช้ ( เพิ่มเติม) การแปลงอย่างชัดเจน?
ก่อนอื่นขอผมลากเส้นง่ายๆระหว่างพวกเขา อย่างเป็นทางการ (แม้ว่าจะเทียบเท่ากับไวยากรณ์ของภาษาก็ตาม) การแคสต์จะเปลี่ยนประเภทในขณะที่การแปลงจะ / อาจเปลี่ยนค่า (ในที่สุดก็รวมกับประเภท) นอกจากนี้การร่ายยังสามารถย้อนกลับได้ในขณะที่การแปลงอาจไม่เป็นเช่นนั้น
หัวข้อนี้ค่อนข้างกว้างดังนั้นลอง จำกัด มันให้แคบลงเล็กน้อยโดยการยกเว้นตัวดำเนินการแคสต์ที่กำหนดเองจากเกม
การร่ายโดยปริยาย
ใน C # การแคสต์มีความหมายโดยนัยเมื่อคุณจะไม่สูญเสียข้อมูลใด ๆ (โปรดทราบว่าการตรวจสอบนี้ดำเนินการกับประเภทไม่ใช่ค่าจริง )
ประเภทดั้งเดิม
ตัวอย่างเช่น:
int tinyInteger = 10;
long bigInteger = tinyInteger;
float tinyReal = 10.0f;
double bigReal = tinyReal;
การแคสต์เหล่านี้มีความหมายโดยนัยเนื่องจากในระหว่างการแปลงคุณจะไม่สูญเสียข้อมูลใด ๆ (คุณแค่ทำให้ประเภทกว้างขึ้น) ในทางกลับกันการแคสต์โดยนัยไม่ได้รับอนุญาตเนื่องจากไม่ว่าค่าจริงจะเป็นเท่าใด (เนื่องจากสามารถตรวจสอบได้เฉพาะในขณะทำงานเท่านั้น) ในระหว่างการแปลงคุณอาจสูญเสียข้อมูลบางอย่าง ตัวอย่างเช่นรหัสนี้จะไม่รวบรวมเนื่องจาก a double
อาจมี (และเป็นจริง) ค่าที่ไม่สามารถแทนได้ด้วยfloat
:
double bigReal = Double.MaxValue;
float tinyReal = bigReal;
วัตถุ
ในกรณีของออบเจ็กต์ (ตัวชี้ไปที่) การส่งจะมีนัยเสมอเมื่อคอมไพเลอร์มั่นใจได้ว่าประเภทซอร์สเป็นคลาสที่ได้รับ (หรือใช้) ประเภทของคลาสเป้าหมายตัวอย่างเช่น:
string text = "123";
IFormattable formattable = text;
NotSupportedException derivedException = new NotSupportedException();
Exception baseException = derivedException;
ในกรณีนี้คอมไพเลอร์รู้ว่าstring
implements IFormattable
และนั่นNotSupportedException
คือ (มาจาก) Exception
ดังนั้น cast จึงเป็นนัย ไม่มีข้อมูลสูญหายเนื่องจากออบเจ็กต์ไม่ได้เปลี่ยนประเภท (ซึ่งแตกต่างกับประเภทstruct
s และ 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) กลับไปที่ตัวอย่างของคุณ:
string text = "123";
double value = (double)text;
ไม่สามารถคอมไพล์ได้เนื่องจากคอมไพเลอร์ไม่สามารถแปลงข้อความเป็นตัวเลขได้ ข้อความอาจมีอักขระใด ๆ ไม่ใช่ตัวเลขเท่านั้นและมีจำนวนมากเกินไปใน C # แม้กระทั่งสำหรับการแคสต์ที่ชัดเจน (แต่อาจได้รับอนุญาตในภาษาอื่น)
วัตถุ
การแปลงจากพอยน์เตอร์ (ไปยังอ็อบเจ็กต์) อาจล้มเหลวหากประเภทไม่เกี่ยวข้องกันเช่นโค้ดนี้จะไม่คอมไพล์ (เนื่องจากคอมไพเลอร์รู้ว่าไม่มีการแปลงที่เป็นไปได้):
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 ที่รู้จักสำหรับสิ่งนั้น) โดยใช้ตัวดำเนินการการแปลงแบบกำหนดเอง (กำหนดไว้ในคลาสที่จะส่ง) หรือกลไกที่ซับซ้อนมากขึ้น ( TypeConverter
s หรือ class method เป็นต้น) คุณไม่ทราบว่าจะเกิดอะไรขึ้นกับสิ่งนั้น แต่คุณทราบดีว่าอาจล้มเหลว (นั่นคือเหตุผลที่ IMO เมื่อมีการแปลงที่ควบคุมได้มากขึ้นคุณควรใช้) ในกรณีของคุณการแปลงจะแยกวิเคราะห์string
เพื่อสร้างdouble
:
double value = Double.Parse(aStringVariable);
แน่นอนว่าสิ่งนี้อาจล้มเหลวดังนั้นหากคุณทำเช่นนั้นคุณควรจับข้อยกเว้นที่อาจโยน ( FormatException
) มันไม่อยู่ในหัวข้อที่นี่ แต่เมื่อมีTryParse
ให้ใช้งานคุณควรใช้มัน (เพราะคุณพูดอย่างมีความหมายมันอาจไม่ใช่ตัวเลขและมันเร็วกว่า ... ที่จะล้มเหลว)
การแปลงใน. NET อาจมาจากหลาย ๆ ที่การแคสTypeConverter
ต์โดยนัย / ชัดแจ้งด้วยตัวดำเนินการ Conversion ที่ผู้ใช้กำหนดการใช้งานIConvertible
และวิธีการแยกวิเคราะห์ (ฉันลืมอะไรไปหรือเปล่า) ดู MSDN สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับพวกเขา
เพื่อจบคำตอบยาว ๆ นี้เพียงไม่กี่คำเกี่ยวกับตัวดำเนินการแปลงที่ผู้ใช้กำหนด เป็นเพียงน้ำตาลที่จะให้โปรแกรมเมอร์ใช้ cast เพื่อแปลงประเภทหนึ่งเป็นอีกประเภทหนึ่ง เป็นวิธีการภายในคลาส (วิธีที่จะถูกแคสต์) ที่ระบุว่า "เฮ้ถ้าเขา / เธอต้องการแปลงประเภทนี้เป็นประเภทนั้นฉันก็ทำได้" ตัวอย่างเช่น:
float? maybe = 10;
float sure1 = (float)maybe;
float sure2 = maybe.Value;
ในกรณีนี้เป็นเรื่องที่ชัดเจนเนื่องจากอาจล้มเหลว แต่จะปล่อยให้นำไปใช้งาน (แม้ว่าจะมีแนวทางเกี่ยวกับเรื่องนี้ก็ตาม) ลองนึกภาพคุณเขียนคลาสสตริงที่กำหนดเองดังนี้:
EasyString text = "123";
double value = (string)text;
ในการนำไปใช้งานของคุณคุณอาจตัดสินใจที่จะ "ทำให้ชีวิตของโปรแกรมเมอร์ง่ายขึ้น" และเปิดเผย 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;
int convertedInteger = Convert.ToInt32(real);
แตกต่างกันใช่มั้ย? นักแสดงจะตัดทอน (เป็นสิ่งที่เราทุกคนคาดหวัง) แต่จะConvert
ทำการปัดเศษเป็นจำนวนเต็มใกล้เคียงที่สุด (และอาจไม่เกิดขึ้นหากคุณไม่ทราบ) วิธีการแปลงแต่ละวิธีนำเสนอความแตกต่างดังนั้นจึงไม่สามารถใช้กฎทั่วไปได้และจะต้องเห็นเป็นกรณี ๆ ไป ... 19 ประเภทฐานในการแปลงเป็นประเภทอื่น ๆ ... รายการอาจค่อนข้างยาวและดีกว่ามากในการปรึกษากรณี MSDN โดย กรณี!