อ่าน 10 นาที
ประเภทสหภาพ
ใน วิทยาการคอมพิวเตอร์ ยูเนียน ( union) คือ ค่า ที่อาจมีรูปแบบหรือการแสดงผลได้หลายแบบในพื้นที่ หน่วยความจำ เดียวกัน หรือ ตัวแปร ที่สามารถเก็บ โครงสร้างข้อมูล ดังกล่าวได้...
ประเภทสหภาพ
ในวิทยาการคอมพิวเตอร์ยูเนียน ( union)คือค่าที่อาจมีรูปแบบหรือการแสดงผลได้หลายแบบในพื้นที่หน่วยความจำ เดียวกัน หรือตัวแปรที่สามารถเก็บโครงสร้างข้อมูล ดังกล่าวได้ ภาษาโปรแกรมบาง ภาษา รองรับประเภทข้อมูลยูเนียน (union type ) สำหรับประเภทข้อมูล นี้ กล่าวอีกนัยหนึ่ง ประเภทข้อมูลยูเนียนระบุประเภทที่อนุญาตให้จัดเก็บในอินสแตนซ์ของมัน เช่นfloatและintในทางตรงกันข้ามกับเรคอร์ด (record ) ซึ่งสามารถกำหนดให้เก็บได้ทั้งค่าทศนิยมและจำนวนเต็ม ยูเนียนสามารถเก็บได้เพียงค่าเดียวในแต่ละครั้ง
ยูเนียนสามารถนึกภาพได้ว่าเป็นส่วนหนึ่งของหน่วยความจำที่ใช้เก็บตัวแปรที่มีชนิดข้อมูลต่างกัน เมื่อมีการกำหนดค่าใหม่ให้กับฟิลด์ ข้อมูลเดิมจะถูกเขียนทับด้วยข้อมูลใหม่ พื้นที่หน่วยความจำที่เก็บค่านั้นไม่มีชนิดข้อมูลเฉพาะตัว (นอกเหนือจากไบต์หรือเวิร์ด ของหน่วยความจำ) แต่ค่าดังกล่าวสามารถถือได้ว่าเป็น ชนิดข้อมูลนามธรรมหลายประเภทโดยมีชนิดข้อมูลเป็นค่าสุดท้ายที่ถูกเขียนลงในพื้นที่หน่วยความจำนั้น
ในทฤษฎีประเภท (type theory ) การรวมกัน (union) มีประเภทผลรวม (sum type ) ซึ่งสอดคล้องกับการรวมกันที่ไม่ทับซ้อนกัน (disjoint union)ในทางคณิตศาสตร์
ขึ้นอยู่กับภาษาและประเภท ค่าของยูเนียนอาจถูกนำไปใช้ในการดำเนินการบางอย่าง เช่นการกำหนดค่าและการเปรียบเทียบความเท่าเทียมกัน โดยไม่จำเป็นต้องทราบประเภทเฉพาะของมัน การดำเนินการอื่นๆ อาจต้องการความรู้เกี่ยวกับประเภทนั้น ไม่ว่าจะโดยข้อมูลภายนอก หรือโดยการใช้ยูเนียนที่มีแท็กกำกับ
สหภาพที่ไม่ได้ติดแท็ก
เนื่องจากข้อจำกัดในการใช้งาน ยูเนียนที่ไม่มีแท็กจึงมักมีให้ใช้งานเฉพาะในภาษาที่ไม่มีการกำหนดประเภทข้อมูล หรือในลักษณะที่ไม่ปลอดภัยต่อประเภทข้อมูล (เช่นในภาษาC ) ข้อดีของยูเนียนที่ไม่มีแท็กเหนือกว่ายูเนียนที่มีแท็กแบบธรรมดาคือ ไม่จำเป็นต้องใช้พื้นที่ในการจัดเก็บแท็กประเภทข้อมูล
ชื่อ "ยูเนียน" มาจากนิยามอย่างเป็นทางการของชนิดข้อมูลนั้น หากพิจารณาชนิดข้อมูลว่าเป็นเซตของค่าทั้งหมดที่ชนิดข้อมูลนั้นสามารถรับได้ ชนิดข้อมูลยูเนียนก็คือการรวมกันทางคณิตศาสตร์ของชนิดข้อมูลที่ประกอบกันขึ้นเป็นยูเนียน เนื่องจากมันสามารถรับค่าใดๆ ก็ได้ที่ฟิลด์ใดๆ ในยูเนียนสามารถรับได้ นอกจากนี้ เนื่องจากยูเนียนทางคณิตศาสตร์จะตัดค่าที่ซ้ำกันออกไป หากฟิลด์มากกว่าหนึ่งฟิลด์ในยูเนียนสามารถรับค่าร่วมกันได้เพียงค่าเดียว ก็เป็นไปไม่ได้ที่จะบอกได้จากค่าเพียงอย่างเดียวว่าฟิลด์ใดถูกเขียนค่าเป็นครั้งสุดท้าย
อย่างไรก็ตาม ฟังก์ชันการเขียนโปรแกรมที่มีประโยชน์อย่างหนึ่งของยูเนียนคือการแปลงองค์ประกอบข้อมูลขนาดเล็กให้เป็นองค์ประกอบข้อมูลขนาดใหญ่เพื่อให้จัดการได้ง่ายขึ้น ตัวอย่างเช่น โครงสร้างข้อมูลที่ประกอบด้วย 4 ไบต์และจำนวนเต็ม 32 บิต สามารถสร้างยูเนียนกับจำนวนเต็ม 64 บิตที่ไม่ระบุเครื่องหมายได้ และทำให้เข้าถึงได้ง่ายขึ้นเพื่อวัตถุประสงค์ในการเปรียบเทียบ เป็นต้น
ยูเนียนในภาษาโปรแกรมต่างๆ
อัลโกล 68
ALGOL 68ได้กำหนดแท็กให้กับยูเนียน และใช้คำสั่ง case clause เพื่อแยกแยะและแยกประเภทองค์ประกอบในระหว่างการทำงาน ยูเนียนที่ประกอบด้วยยูเนียนอื่นจะถูกมองว่าเป็นเซตของความเป็นไปได้ทั้งหมดขององค์ประกอบ และหากบริบทกำหนด ยูเนียนนั้นจะถูกแปลงเป็นยูเนียนที่กว้างกว่าโดยอัตโนมัติ ยูเนียนอาจไม่มีค่าใด ๆ ซึ่งสามารถแยกแยะได้ในระหว่างการทำงาน ตัวอย่างเช่น:
โหมดโหนด = ยูเนียน ( เรียล , อินท์ , สตริง , วอยด์ ); node n := "abc"; กรณี n ใน ( real r): พิมพ์ (("real:", r)) ( int i): print(("int:", i)), ( สตริง s): พิมพ์ (("สตริง:", s)) ( void ): print(("void:", "EMPTY")), out print(("?:", n)) esacไวยากรณ์ของประเภทสหภาพ C/C++ และแนวคิดของการแปลงประเภทมาจาก ALGOL 68 แม้ว่าจะอยู่ในรูปแบบที่ไม่มีแท็กก็ตาม[ 1 ]
ซี/ซี++
ในภาษา CและC++ยูเนียนที่ไม่มีแท็กจะถูกเขียนในลักษณะเดียวกับโครงสร้าง ( struct ) เกือบทุกประการ ยกเว้นว่าสมาชิกข้อมูลแต่ละตัวจะอยู่ที่ตำแหน่งหน่วยความจำเดียวกัน สมาชิกข้อมูลนั้น เช่นเดียวกับในโครงสร้าง ไม่จำเป็นต้องเป็นค่าพื้นฐาน และในความเป็นจริงอาจเป็นโครงสร้างหรือแม้แต่ยูเนียนอื่นๆ ก็ได้ ภาษา C++ (ตั้งแต่C++11 ) ยังอนุญาตให้สมาชิกข้อมูลเป็นชนิดใดก็ได้ที่มีคอนสตรัคเตอร์/ดีสตรัคเตอร์ที่สมบูรณ์ และ/หรือคอนสตรัคเตอร์สำเนา หรือตัวดำเนินการกำหนดค่าสำเนาที่ไม่ธรรมดา ตัวอย่างเช่น เป็นไปได้ที่จะมีสตริง มาตรฐานของ C++ เป็นสมาชิกของยูเนียน
การใช้งานหลักของยูเนียนคือการอนุญาตให้เข้าถึงตำแหน่งทั่วไปโดยใช้ข้อมูลประเภทต่างๆ เช่น การเข้าถึงอินพุต/เอาต์พุตของฮาร์ดแวร์ การแบ่งปันบิตฟิลด์และเวิร์ด หรือการเล่นคำประเภทข้อมูล ยูเนียนยังสามารถให้ โพลีมอร์ฟิซึมระดับต่ำได้อีกด้วย อย่างไรก็ตาม ไม่มีการตรวจสอบประเภทข้อมูล ดังนั้นจึงขึ้นอยู่กับโปรแกรมเมอร์ที่จะต้องตรวจสอบให้แน่ใจว่ามีการเข้าถึงฟิลด์ที่ถูกต้องในบริบทต่างๆ ฟิลด์ที่เกี่ยวข้องของตัวแปรยูเนียนมักจะถูกกำหนดโดยสถานะของตัวแปรอื่นๆ ซึ่งอาจอยู่ในโครงสร้างที่ครอบคลุมอยู่
รูปแบบการเขียนโปรแกรมภาษา C ทั่วไปอย่างหนึ่งคือการใช้ยูเนียนเพื่อทำสิ่งที่ C++ เรียกว่า "การreinterpret_castหาค่าที่แน่นอน" โดยการกำหนดค่าให้กับฟิลด์หนึ่งของยูเนียนและอ่านค่าจากอีกฟิลด์หนึ่ง ดังเช่นที่ทำในโค้ดที่ขึ้นอยู่กับการแสดงค่าแบบดิบๆ ตัวอย่างที่เป็นรูปธรรมคือวิธีการคำนวณรากที่สองโดยใช้การแสดงค่าแบบ IEEEอย่างไรก็ตาม นี่ไม่ใช่การใช้ยูเนียนที่ปลอดภัยโดยทั่วไป
ตัวระบุโครงสร้างและยูเนียนมีรูปแบบเดียวกัน [ . . . ] ขนาดของยูเนียนนั้นเพียงพอที่จะบรรจุสมาชิกที่ใหญ่ที่สุดของมันได้ ค่าของสมาชิกอย่างมากที่สุดหนึ่งตัวเท่านั้นที่สามารถเก็บไว้ในออบเจ็กต์ ยูเนียน ได้ในเวลาใดเวลาหนึ่ง ตัวชี้ไปยังออบเจ็กต์ยูเนียนที่แปลงอย่างเหมาะสม จะชี้ไปยังสมาชิกแต่ละตัวของมัน (หรือถ้าสมาชิกเป็นบิตฟิลด์ ก็จะชี้ไปยังหน่วยที่มันอยู่) และในทางกลับกัน
— มาตรฐาน ANSI/ISO 9899:1990 (มาตรฐาน ANSI C) มาตรา 6.5.2.1
สหภาพที่ไม่เปิดเผยตัวตน
ใน C++, C11และส่วนขยายที่ไม่เป็นมาตรฐานในคอมไพเลอร์หลายตัว ยูเนียนยังสามารถเป็นแบบไม่ระบุชื่อได้ สมาชิกข้อมูลไม่จำเป็นต้องอ้างอิง แต่สามารถเข้าถึงได้โดยตรง ยูเนียนมีข้อจำกัดบางประการเมื่อเทียบกับยูเนียนแบบดั้งเดิม: ใน C11 ยูเนียนต้องเป็นสมาชิกของโครงสร้างหรือยูเนียนอื่น[ 2 ]และใน C++ ยูเนียนไม่สามารถมีเมธอดหรือตัวระบุการเข้าถึงได้
การละเว้นส่วนชื่อคลาสในไวยากรณ์ไม่ได้ทำให้ยูเนียนนั้นกลายเป็นยูเนียนนิรนามเสมอไป ยูเนียนที่จะมีคุณสมบัติเป็นยูเนียนนิรนามได้นั้น การประกาศต้องไม่ประกาศอ็อบเจ็กต์ ตัวอย่าง:
union { float f ; uint32_t d ; // สมมติว่า float มีความกว้าง 32 บิต};f = 3.14f ; printf ( "การแสดงค่าเลขฐานสิบหกของ 3.14f: %x \n " , u . d );ยูเนียนนิรนามยังมีประโยชน์ในstructคำจำกัดความ C เพื่อให้เกิดความรู้สึกถึงเนมสเปซ[ 3 ]
สหภาพที่โปร่งใส
ในคอมไพเลอร์เช่น GCC, Clang และ IBM XL C สำหรับ AIX transparent_unionมีแอตทริบิวต์สำหรับประเภทสหภาพ ประเภทที่อยู่ในสหภาพสามารถแปลงเป็นประเภทสหภาพเองได้อย่างโปร่งใสในการเรียกฟังก์ชัน โดยมีเงื่อนไขว่าประเภททั้งหมดต้องมีขนาดเท่ากัน โดยส่วนใหญ่มีไว้สำหรับฟังก์ชันที่มีอินเทอร์เฟซพารามิเตอร์หลายตัว ซึ่งเป็นการใช้งานที่จำเป็นเนื่องจากส่วนขยาย Unix ในยุคแรกและการกำหนดมาตรฐานใหม่ในภายหลัง[ 4 ]
โคบอล
ในภาษา COBOLการกำหนดรายการข้อมูลแบบยูเนียนทำได้สองวิธี วิธีแรกใช้ คีย์เวิร์ด RENAMES (ระดับ 66) ซึ่งเป็นการแมปรายการข้อมูลตัวอักษรและตัวเลขตัวที่สองไว้บนตำแหน่งหน่วยความจำเดียวกันกับรายการข้อมูลก่อนหน้า ในตัวอย่างโค้ดด้านล่าง รายการข้อมูลPERSON-RECถูกกำหนดให้เป็นกลุ่มที่ประกอบด้วยกลุ่มอื่นและรายการข้อมูลตัวเลขPERSON-DATAถูกกำหนดให้เป็นรายการข้อมูลตัวอักษรและตัวเลขที่เปลี่ยนชื่อจากPERSON-RECโดยถือว่าไบต์ข้อมูลที่ต่อเนื่องอยู่ภายในนั้นเป็นข้อมูลอักขระ
01 PERSON-REC . 05 PERSON-NAME . 10 PERSON-NAME-LAST PIC X(12) . 10 PERSON-NAME-FIRST PIC X(16) . 10 PERSON-NAME-MID PIC X . 05 PERSON-ID PIC 9(9) PACKED-DECIMAL . 01 PERSON-DATA RENAMES PERSON-REC .วิธีที่สองในการกำหนดประเภทแบบยูเนียนคือการใช้ คีย์เวิร์ด REDEFINESในตัวอย่างโค้ดด้านล่าง ข้อมูลVERS-NUMถูกกำหนดให้เป็นจำนวนเต็มไบนารี 2 ไบต์ที่มีหมายเลขเวอร์ชัน ข้อมูลตัวที่สองVERS-BYTESถูกกำหนดให้เป็นตัวแปรตัวอักษรและตัวเลข 2 ตัว เนื่องจากข้อมูลตัวที่สองถูกกำหนดใหม่ทับข้อมูลตัวแรก ข้อมูลทั้งสองจึงใช้ที่อยู่หน่วยความจำเดียวกัน และด้วยเหตุนี้จึงใช้ไบต์ข้อมูลพื้นฐานเดียวกัน ข้อมูลตัวแรกจะตีความไบต์ข้อมูล 2 ไบต์เป็นค่าไบนารี ในขณะที่ข้อมูลตัวที่สองจะตีความไบต์เหล่านั้นเป็นค่าอักขระ
01 VERS-INFO . 05 VERS-NUM PIC S9(4) COMP . 05 VERS-BYTES PIC X(2) REDEFINES VERS-NUMปาสคาล
ในภาษาปาสคาลมีสองวิธีในการสร้างยูเนียน วิธีแรกคือวิธีมาตรฐานโดยใช้เรคอร์ดแบบแปรผัน (variant record) วิธีที่สองคือวิธีที่ไม่เป็นมาตรฐาน โดยการประกาศตัวแปรเป็นแบบสัมบูรณ์ (absolute variable) ซึ่งหมายความว่าตัวแปรนั้นจะถูกวางไว้ในตำแหน่งหน่วยความจำเดียวกันกับตัวแปรอื่น หรือที่แอดเดรสแบบสัมบูรณ์ แม้ว่าคอมไพเลอร์ของปาสคาลทุกตัวจะรองรับเรคอร์ดแบบแปรผัน แต่มีเพียงบางตัวเท่านั้นที่รองรับตัวแปรแบบสัมบูรณ์
สำหรับตัวอย่างนี้ ข้อมูลต่อไปนี้เป็นประเภทจำนวนเต็มทั้งหมด: ไบต์ประกอบด้วย 8 บิต, เวิร์ดมี 16 บิต และจำนวนเต็ม มี 32 บิต
ตัวอย่างต่อไปนี้แสดงรูปแบบสัมบูรณ์ที่ไม่เป็นมาตรฐาน:
var A : จำนวนเต็ม; B : อาร์เรย์[ 1 .. 4 ] ของไบต์สัมบูรณ์A ; C : จำนวนเต็มสัมบูรณ์0 ;ในตัวอย่างแรก แต่ละองค์ประกอบของอาร์เรย์ B จะถูกแมปไปยังไบต์เฉพาะของตัวแปร A ในตัวอย่างที่สอง ตัวแปร C จะถูกกำหนดให้มีค่าเป็นแอดเดรสเครื่องที่ 0 อย่างแม่นยำ
ในตัวอย่างต่อไปนี้ ข้อมูลหนึ่งรายการมีหลายรูปแบบ ซึ่งบางรูปแบบมีตำแหน่งที่ตั้งเดียวกันกับรูปแบบอื่นๆ:
ประเภทรูปร่าง= ( วงกลม, สี่เหลี่ยมจัตุรัส, สามเหลี่ยม) ; ขนาด= บันทึกกรณีรูปภาพ: รูปร่างของวงกลม: ( เส้นผ่านศูนย์กลาง: จำนวนจริง) ; สี่เหลี่ยมจัตุรัส: ( ความกว้าง: จำนวนจริง) ; สามเหลี่ยม: ( ด้าน: จำนวนจริง; มุม 1 , มุม 2 : 0 .. 360 ) สิ้นสุด;พีแอล/ไอ
ในPL/Iคำศัพท์ดั้งเดิมสำหรับยูเนียนคือcell [ 5 ] ซึ่งยังคงได้รับการยอมรับว่าเป็นคำพ้องความหมายของยูเนียนโดยคอมไพเลอร์หลายตัว การประกาศยูเนียนนั้นคล้ายกับการนิยามโครงสร้าง โดยที่องค์ประกอบในระดับเดียวกันภายในการประกาศยูเนียนจะใช้พื้นที่จัดเก็บเดียวกัน องค์ประกอบของยูเนียนสามารถเป็นชนิดข้อมูลใดก็ได้ รวมถึงโครงสร้างและอาร์เรย์[ 6 ] : หน้า 192–193 ในที่นี้ vers_num และ vers_bytes ใช้พื้นที่จัดเก็บเดียวกัน
1 vers_info union , 5 vers_num fixed binary , 5 vers_bytes pic '(2)A';ทางเลือกอื่นนอกเหนือจากการประกาศยูเนียนคือแอตทริบิวต์ DEFINED ซึ่งอนุญาตให้ประกาศการจัดเก็บทางเลือกได้ อย่างไรก็ตามชนิดข้อมูลของตัวแปรพื้นฐานและตัวแปรที่กำหนดต้องตรงกัน[ 6 ] : หน้า 289–293
สนิม
Rustใช้ทั้งยูเนียนแบบมีแท็กและไม่มีแท็ก ใน Rust ยูเนียนแบบมีแท็กจะถูกใช้งานโดยใช้enumคีย์เวิร์ด ซึ่งแตกต่างจากประเภทแจงนับในภาษาอื่นๆ ส่วนใหญ่ ตัวแปร enum ใน Rust สามารถมีข้อมูลเพิ่มเติมในรูปแบบของทูเปิลหรือโครงสร้าง ทำให้พวกมันเป็นยูเนียนแบบมีแท็กแทนที่จะเป็นประเภทแจงนับธรรมดา[ 7 ]
Rust ยังรองรับยูเนียนที่ไม่มีแท็กโดยใช้unionคีย์เวิร์ด โดยค่าเริ่มต้น รูปแบบหน่วยความจำของยูเนียนใน Rust จะไม่ถูกกำหนด[ 8 ]แต่ยูเนียนที่มี#[repr(C)]แอตทริบิวต์จะถูกจัดวางในหน่วยความจำเหมือนกับยูเนียนที่เทียบเท่าในภาษา C ทุกประการ[ 9 ]การอ่านฟิลด์ของยูเนียนสามารถทำได้ภายในunsafeฟังก์ชันหรือบล็อกเท่านั้น เนื่องจากคอมไพเลอร์ไม่สามารถรับประกันได้ว่าข้อมูลในยูเนียนจะถูกต้องสำหรับประเภทของฟิลด์ หากไม่เป็นเช่นนั้น จะส่งผลให้เกิดพฤติกรรมที่ไม่กำหนด[ 10 ]
ไวยากรณ์และตัวอย่าง
ซี
ในภาษาซี ไวยากรณ์จะเป็นดังนี้:
ยูเนียน< name > { < datatype > < ชื่อตัวแปรที่1 > ; < datatype > < ชื่อตัวแปรที่2 > ; . . . < datatype > < ชื่อตัวแปรที่ n > ; } < ชื่อตัวแปรยูเนียน> ;โครงสร้างหนึ่งๆ ก็สามารถเป็นสมาชิกของสหภาพได้เช่นกัน ดังตัวอย่างต่อไปนี้:
ยูเนียน{ struct { int a ; float b ; char c ; } svar ; int d ; } uvar ;ตัวอย่างนี้กำหนดตัวแปรuvarเป็นยูเนียนซึ่งประกอบด้วยสมาชิกสองตัว ได้แก่ โครงสร้างชื่อsvar(ซึ่งประกอบด้วยสมาชิกสามตัว) และตัวแปรจำนวนเต็มdชื่อ
การรวมตัวกันอาจเกิดขึ้นภายในโครงสร้างและอาร์เรย์ และในทางกลับกัน:
#define N 10000struct { int flags ; char * name ; int utype ; union { int ival ; float fval ; char * sval ; } u ; } symtab [ N ];ตัวเลขivalจะถูกอ้างอิงด้วยsymtab[i].u.ivalและสตริง จะ ถูก svalอ้างอิงด้วยsymtab[i].u.sval
ซี++
C++ รองรับยูเนียนเช่นเดียวกับ C แต่การใช้งานอย่างปลอดภัยใน C++ นั้นทำได้ยาก สำหรับวัตถุประสงค์หลักสองประการของยูเนียน C++ ที่เป็นสำนวนมีทางเลือกอื่น: [ 11 ] [ 12 ]
- การแปลงประเภทข้อมูลโดยใช้ยูเนียน เช่นการแปลงค่าทศนิยมเป็นค่าไบนารีในรูปแบบจำนวนเต็ม สามารถทำได้ด้วย.
union{uint32_tx;floaty;}reinterpret_cast - สำหรับประเภทที่ไม่ใช่PODนั้น ยูเนียนจำเป็นต้องมีการสร้างและการทำลายอย่างชัดเจน ทำให้ใช้งานได้อย่างปลอดภัยได้ยาก
std::variantซึ่งถูกนำมาใช้ในC++17ทำหน้าที่เหมือนยูเนียนแบบแท็กจึงหลีกเลี่ยงความยุ่งยากนี้ได้
#include <cmath> #include <cstdint> #include <stdexcept> #include <variant>uint32_t encode_float_as_binary ( float f ) noexcept { return reinterpret_cast < uint32_t &> ( f ); }bool is_integral ( std :: variant < int , float >& v ) { if ( int * _ = std :: get_if < int > ( & v )) { return true ; } else if ( float * f = std :: get_if < float > ( & v )) { return std :: modff ( * f , nullptr ) == 0 ; } throw std :: invalid_argument ( "Invalid variant input!" ); }ซี#
ก่อน C# 15 ไม่มีประเภทข้อมูลแบบยูเนียนในภาษา C# วิธีที่ใกล้เคียงที่สุดในการเลียนแบบคือการใช้เรคอร์ดและการ จับคู่รูปแบบ
เนมสเปซWikipedia.Examples ;โดยใช้ระบบ;บันทึกนามธรรมรูปร่าง; บันทึกวงกลม( รัศมีสองเท่า) : รูปร่าง; บันทึกสี่เหลี่ยมผืนผ้า( ความกว้างสองเท่า, ความสูงสองเท่า) : รูปร่าง;public class Example { public static double Area ( Shape s ) => s switch { Circle c => Math . PI * c . Radius * c . Radius , Rectangle r => r . Width * r . Height , _ => throw new NotSupportedException () }; }นอกจากนี้ยังสามารถจำลองได้อย่างใกล้เคียงโดยใช้โครงสร้างระดับต่ำ ตัวอย่างต่อไปนี้แสดงให้เห็นถึงการรวมกันดังกล่าว ซึ่งฟิลด์ต่างๆ ใช้หน่วยความจำเดียวกัน แต่ไม่ปลอดภัยในแง่ของชนิดข้อมูล เหมาะสำหรับใช้ในการทำงานร่วมกัน การแปลงเป็นรูปแบบอนุกรม และการจัดการที่ไม่ปลอดภัย
การใช้งานSystem.Runtime.InteropServices ;// ใกล้เคียงกับ C ที่สุด union IntFloatUnion { int i; float f; }; [StructLayout(LayoutKind.Explicit)] struct IntFloatUnion { [FieldOffset(0)] public int IntValue ;[FieldOffset(0)] public float FloatValue ; }ใน C# 15 ได้มีการนำยูเนียนมาใช้ในภาษา ดังที่สามารถแสดงได้ดังต่อไปนี้: [ 13 ]
public record class Car ( string Model ); public record class Bicycle ( string Model ); public record class Bus ( string Model );ยานพาหนะของสหภาพสาธารณะ( รถยนต์จักรยานรถบัส)Vehicle car = new Car ( "Tesla Model 3" ); Console . WriteLine ( car . Value ); // Car { Model = Tesla Model 3 }Vehicle bike = new Bicycle ( "Giant Escape 3" ); Console . WriteLine ( bike . Value ); // Bicycle { Model = Giant Escape 3 }Vehicle bus = new Bus ( "Volvo 9700" ); Console . WriteLine ( bus . Value ); // Bus { Model = Volvo 9700 }Vehicle v = /* ยานพาหนะบางคันที่นี่ */ ; string model = v switch { Car c => c . Model , Bicycle bk => b . Model , Bus bs => bs . Model , };ค่าdefaultของยูเนียนคือnullแต่ถ้าชนิดข้อมูลทั้งหมดในยูเนียนไม่สามารถเป็นค่าว่าง ได้ ก็switchไม่จำเป็นต้องตรวจสอบค่าว่างในnullนิพจน์
ชวา
ในภาษา Java ไม่มีประเภทข้อมูลแบบยูเนียน (Union types) แต่สามารถจำลองได้บ้างโดยใช้อินเทอร์เฟซ
// "Shape" สามารถคิดได้ว่าเป็นผลรวมของ "Circle", "Rectangle" และประเภทอื่นๆ ที่ใช้งาน "Shape" // เราสามารถจำกัดสิ่งที่อยู่ในผลรวมได้อย่างชัดเจนโดยใช้ sealed interface sealed interface Shape permits Circle , Rectangle {}บันทึกวงกลม( รัศมีสองเท่า) ใช้งานรูปร่าง{}record Rectangle ( double width , double height ) implements Shape {}คลาสExample { double area ( Shape s ) { return switch ( s ) { case Circle ( double r ) -> Math . PI * r * r ; case Rectangle ( double w , double h ) -> w * h ; }; } }ข้อความcatchสามารถประกาศการรวมกันของประเภทข้อยกเว้นหลายประเภท ไวยากรณ์นี้คล้ายกับไวยากรณ์ประเภทสหภาพในภาษาอื่น ๆ แต่ไม่ได้สร้างประเภทใหม่ขึ้นมาจริง ๆ ประเภทที่มีประสิทธิภาพของพารามิเตอร์ข้อยกเว้นคือซูเปอร์ไทป์ทั่วไปที่เฉพาะเจาะจงที่สุดของทางเลือก ข้อความ catch จะดักจับเฉพาะข้อยกเว้นของประเภทที่ประกาศไว้เท่านั้น ไม่ใช่คลาสย่อยอื่น ๆ ของซูเปอร์ไทป์ทั่วไป[ 14 ]
คลาสFooException สืบทอดมาจากRuntimeException { String name () { return "Foo" ; } }คลาสBarException สืบทอดมาจากRuntimeException { String name () { return "Bar" ; } }void main () { try { throw Math.random ( ) < 0.5 ? new FooException () : new BarException (); } catch ( FooException | BarException e ) { // e มีประเภทเป็น RuntimeException ซึ่ง เป็นซูเปอร์ไทป์ทั่วไปของ FooException และ BarException System.out.println(e); e.printStackTrace ( ) ; // System.out.println ( e.name ( ) ) ; // เนื่องจากไม่มีเมธอด name() ใน RuntimeException จึงทำให้คอมไพล์ไม่สำเร็จ} }พีพี
ประเภท Union ได้รับการแนะนำใน PHP 8.0 [ 15 ]ค่าต่างๆ จะถูก "ติดแท็ก" ด้วยประเภทโดยปริยายโดยภาษา และสามารถเรียกได้โดย "gettype()"
คลาสExample { private int | float $foo ;public function squareAndAdd ( float | int $bar ) : int | float { return $bar ** 2 + $this -> foo ; } }ไพธอน
การรองรับการกำหนดประเภทได้รับการแนะนำใน Python 3.5 [ 16 ]ไวยากรณ์ใหม่สำหรับประเภทสหภาพได้รับการแนะนำใน Python 3.10 [ 17 ]
จากการพิมพ์นำเข้าสหภาพตัวอย่างคลาส: foo : int = 0# รูปแบบเก่า: def square_and_add ( self , bar : Union [ int , float ]) -> Union [ int , float ]: return bar ** 2 + self . foo# รูปแบบใหม่: def square_and_add ( self , bar : int | float ) -> int | float : return bar ** 2 + self . fooไทป์สคริปต์
TypeScript รองรับประเภท Union [ 18 ]ค่าต่างๆ จะถูก "ติดแท็ก" ด้วยประเภทโดยปริยายโดยภาษา และสามารถเรียกได้โดยใช้typeofการเรียกสำหรับค่าพื้นฐานและinstanceofการเปรียบเทียบสำหรับประเภทข้อมูลที่ซับซ้อน ประเภทที่มีการใช้งานทับซ้อนกัน (เช่น เมธอด slice มีอยู่ในทั้งสตริงและอาร์เรย์ ตัวดำเนินการบวกทำงานได้ทั้งกับสตริงและตัวเลข) ไม่จำเป็นต้องจำกัดเพิ่มเติมเพื่อใช้คุณสมบัติเหล่านี้
ฟังก์ชันsuccessor ( n : number | bigint ) : number | bigint { // ชนิดข้อมูลที่รองรับการดำเนินการเดียวกันไม่จำเป็นต้องลดขนาดreturn ++ n ; }ฟังก์ชันdependsOnParameter ( v : string | string [] | number ) { // ประเภทที่แตกต่างกันจำเป็นต้องจำกัดขอบเขตหาก( v instanceof Array ) { // ทำบางอย่าง} else if ( typeof ( v ) === "string" ) { // ทำอย่างอื่น} else { // ต้องเป็นตัวเลข} }สนิม
ยูเนียนที่มีแท็กใน Rust ใช้enumคีย์เวิร์ด `tagged` และสามารถประกอบด้วยทูเพิลและโครงสร้างข้อมูลแบบ struct ได้:
enum Foo { Bar ( i32 ), Baz { x : String , y : i32 }, }ยูเนียนที่ไม่มีแท็กใน Rust ใช้unionคีย์เวิร์ด:
ยูเนียนFoo { bar : i32 , baz : bool , }การอ่านข้อมูลจากฟิลด์ของยูเนียนที่ไม่มีแท็กจะส่งผลให้เกิดพฤติกรรมที่ไม่แน่นอนหากข้อมูลในยูเนียนไม่ถูกต้องตามประเภทของฟิลด์ และจึงจำเป็นต้องใช้unsafeบล็อก:
let x = Foo { bar : 10 }; let y = unsafe { x . bar }; // โค้ดนี้จะกำหนดค่า y เป็น 10 และไม่ทำให้เกิดพฤติกรรมที่ไม่แน่นอนlet z = unsafe { x . baz }; // โค้ดนี้จะทำให้เกิดพฤติกรรมที่ไม่แน่นอน เนื่องจากค่าที่เก็บไว้ใน x ไม่ใช่ค่าบูลีนที่ถูกต้องลิงก์ภายนอก
- boost::variantทางเลือกที่ปลอดภัยต่อประเภทข้อมูลแทน union ใน C++
- MSDN: คลาส โครงสร้าง และยูเนียนสำหรับตัวอย่างและไวยากรณ์
- ความแตกต่างความแตกต่างระหว่างสหภาพและโครงสร้าง
- ความแตกต่างระหว่าง struct และ union ในภาษา C++
สรุปเนื้อหา
ข้อมูลสำคัญจากบทความ
ข้อมูลสำคัญเกี่ยวกับ ประเภทสหภาพ
ใน วิทยาการคอมพิวเตอร์ ยูเนียน ( union) คือ ค่า ที่อาจมีรูปแบบหรือการแสดงผลได้หลายแบบในพื้นที่ หน่วยความจำ เดียวกัน หรือ ตัวแปร ที่สามารถเก็บ โครงสร้างข้อมูล ดังกล่าวได้...
สหภาพที่ไม่ได้ติดแท็ก
เนื่องจากข้อจำกัดในการใช้งาน ยูเนียนที่ไม่มีแท็กจึงมักมีให้ใช้งานเฉพาะในภาษาที่ไม่มีการกำหนดประเภทข้อมูล หรือในลักษณะที่ไม่ปลอดภัยต่อประเภทข้อมูล (เช่นในภาษา C ) ข้อดีของยูเนียนที่ไม่มีแท็กเหนือกว่ายูเนียนที่มีแท็กแบบธรรมดาคือ...
อัลโกล 68
ALGOL 68 ได้กำหนดแท็กให้กับยูเนียน และใช้คำสั่ง case clause เพื่อแยกแยะและแยกประเภทองค์ประกอบในระหว่างการทำงาน ยูเนียนที่ประกอบด้วยยูเนียนอื่นจะถูกมองว่าเป็นเซตของความเป็นไปได้ทั้งหมดขององค์ประกอบ และหากบริบทกำหนด...
ซี/ซี++
ใน ภาษา C และ C++ ยูเนียนที่ไม่มีแท็กจะถูกเขียนในลักษณะเดียวกับโครงสร้าง ( struct ) เกือบทุกประการ ยกเว้นว่าสมาชิกข้อมูลแต่ละตัวจะอยู่ที่ตำแหน่งหน่วยความจำเดียวกัน สมาชิกข้อมูลนั้น เช่นเดียวกับในโครงสร้าง ไม่จำเป็นต้องเป็นค่าพื้นฐาน...