ทำไมเราต้องใช้การชกมวยและการเลิกทำกล่องใน C #


323

ทำไมเราต้องใช้การชกมวยและการเลิกทำกล่องใน C #

ฉันรู้ว่ามวยและ unboxing คืออะไร แต่ฉันไม่สามารถเข้าใจการใช้งานจริงของมัน ทำไมและที่ไหนฉันจึงควรใช้

short s = 25;

object objshort = s;  //Boxing

short anothershort = (short)objshort;  //Unboxing

คำตอบ:


480

ทำไม

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

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

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

ฉันควรใช้ที่ไหน

ตัวอย่างเช่นประเภทคอลเลกชันเก่าArrayListกินobjects เท่านั้น นั่นคือมันเก็บเฉพาะการอ้างอิงถึงสิ่งที่อาศัยอยู่ที่ไหนสักแห่ง หากไม่มีมวยคุณไม่สามารถใส่ชุดintดังกล่าวได้ แต่ด้วยการชกมวยคุณสามารถ

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

สิ่งนี้ถูกต้อง:

double e = 2.718281828459045;
int ee = (int)e;

มันไม่ใช่:

double e = 2.718281828459045;
object o = e; // box
int ee = (int)o; // runtime exception

แต่คุณต้องทำสิ่งนี้:

double e = 2.718281828459045;
object o = e; // box
int ee = (int)(double)o;

ครั้งแรกที่เราได้อย่างชัดเจน unbox double( (double)o) intแล้วโยนไปยังที่

ผลลัพธ์ของสิ่งต่อไปนี้คืออะไร:

double e = 2.718281828459045;
double d = e;
object o1 = d;
object o2 = e;
Console.WriteLine(d == e);
Console.WriteLine(o1 == o2);

ลองคิดดูสักครู่ก่อนจะไปยังประโยคถัดไป

ถ้าคุณพูดTrueและFalseยอดเยี่ยม! รออะไร? นั่นเป็นเพราะ==ประเภทการอ้างอิงใช้การอ้างอิงความเท่าเทียมกันซึ่งจะตรวจสอบว่าการอ้างอิงมีความเท่าเทียมกันหรือไม่ถ้าค่าอ้างอิงนั้นเท่ากัน นี่เป็นข้อผิดพลาดที่ง่ายที่จะทำอันตราย บางทีอาจจะลึกซึ้งยิ่งขึ้น

double e = 2.718281828459045;
object o1 = e;
object o2 = e;
Console.WriteLine(o1 == o2);

จะพิมพ์False!

ดีกว่าที่จะพูดว่า:

Console.WriteLine(o1.Equals(o2));

ซึ่งจะแล้วขอบคุณมาก ๆ Trueพิมพ์

หนึ่งความละเอียดสุดท้าย:

[struct|class] Point {
    public int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

Point p = new Point(1, 1);
object o = p;
p.x = 2;
Console.WriteLine(((Point)o).x);

ผลลัพธ์คืออะไร มันขึ้นอยู่กับ! หากPointเป็นเช่นstructนั้นผลลัพธ์จะเป็น1แต่ถ้าPointเป็นเช่นclassนั้นผลลัพธ์จะเป็น2! การแปลงมวยทำสำเนาของค่าที่บรรจุอยู่เพื่ออธิบายความแตกต่างของพฤติกรรม


@ Jason คุณหมายถึงว่าถ้าเรามีรายการดั้งเดิมไม่มีเหตุผลที่จะใช้มวย / unboxing?
Pacerier

ฉันไม่แน่ใจว่าสิ่งที่คุณหมายถึงโดย "รายการดั้งเดิม"
jason

3
คุณช่วยพูดคุยกับผลการปฏิบัติงานของboxingและunboxing?
เควินเมเรดิ ธ

@KevinMeredith มีคำอธิบายพื้นฐานเกี่ยวกับประสิทธิภาพของการชกมวยและการยกเลิก
InfZero

2
คำตอบที่ยอดเยี่ยม - ดีกว่าคำอธิบายส่วนใหญ่ที่ฉันได้อ่านในหนังสือที่ได้รับการยกย่องอย่างดี
FredM

58

ในกรอบงาน. NET มีชนิดของสองชนิดคือชนิดของค่าและชนิดของการอ้างอิง นี่เป็นเรื่องธรรมดาในภาษา OO

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

ตอนนี้ย้อนกลับไปในสมัยก่อน (1.0 ของ Microsoft.NET) ไม่มีฮัลลาบุลทั่วไปชื่อใหม่ คุณไม่สามารถเขียนวิธีที่มีอาร์กิวเมนต์เดียวที่สามารถให้บริการประเภทค่าและประเภทการอ้างอิงได้ นั่นเป็นการละเมิดความหลากหลาย ดังนั้นมวยถูกนำมาใช้เป็นวิธีการบังคับประเภทของค่าลงในวัตถุ

หากไม่สามารถทำได้กรอบจะเกลื่อนไปด้วยวิธีการและชั้นเรียนที่มีจุดประสงค์เพียงเพื่อรับสายพันธุ์อื่น ๆ ไม่เพียงแค่นั้น แต่เนื่องจากประเภทค่าไม่ได้แชร์บรรพบุรุษประเภททั่วไปอย่างแท้จริงคุณจะต้องมีวิธีการโอเวอร์โหลดที่แตกต่างกันสำหรับแต่ละประเภทค่า (บิต, ไบต์, int16, int32 ฯลฯ ฯลฯ )

ชกมวยป้องกันไม่ให้สิ่งนี้เกิดขึ้น และนั่นเป็นเหตุผลว่าทำไมอังกฤษจึงฉลองวัน Boxing Day


1
ก่อนการบังคับใช้มวยอัตโนมัติจำเป็นต้องทำหลายสิ่งหลายอย่าง เมื่อพิจารณาถึงการมีอยู่ของยาชื่อสามัญอย่างไรก็ตามไม่จำเป็นต้องรักษาความเข้ากันได้กับรหัสเดิมฉันคิดว่า. net น่าจะดีกว่าหากไม่มีการแปลงมวยโดยนัย การคัดเลือกประเภทของมูลค่าList<string>.Enumeratorจะให้IEnumerator<string>ผลกับวัตถุซึ่งส่วนใหญ่ทำตัวเหมือนคลาสประเภท แต่ด้วยEqualsวิธีการที่แตก วิธีที่ดีกว่าในการส่งList<string>.Enumeratorต่อไปที่IEnumerator<string>จะเรียกใช้ตัวดำเนินการแปลงที่กำหนดเอง แต่การมีอยู่ของการแปลงโดยนัยจะป้องกันไม่ให้
supercat

42

วิธีที่ดีที่สุดที่จะเข้าใจสิ่งนี้คือการดูภาษาการเขียนโปรแกรมระดับล่าง C # ประกอบ

ในภาษาระดับต่ำสุดเช่น C ตัวแปรทั้งหมดมีที่เดียว: The Stack ทุกครั้งที่คุณประกาศตัวแปรมันจะไปที่สแต็ก พวกเขาสามารถเป็นค่าดั้งเดิมเช่นบูล, ไบต์, int 32- บิต, uint 32 บิต ฯลฯ สแต็คมีทั้งง่ายและรวดเร็ว เมื่อตัวแปรถูกเพิ่มเข้าไปพวกมันจะอยู่ข้างบนอีกอันหนึ่งดังนั้นอันดับแรกที่คุณประกาศจะอยู่ที่ 0x00, ถัดไปที่ 0x01, ถัดไปที่ 0x02 ใน RAM, ฯลฯ นอกจากนี้ตัวแปรมักจะถูกแก้ไขล่วงหน้าที่คอมไพล์ - เวลาดังนั้นที่อยู่ของพวกเขาจึงเป็นที่รู้จักกันก่อนที่คุณจะรันโปรแกรม

ในระดับต่อไปเช่น C ++ โครงสร้างหน่วยความจำที่สองที่เรียกว่า Heap ถูกนำเสนอ คุณยังคงอาศัยอยู่ในสแต็กเป็นส่วนใหญ่ แต่ ints พิเศษที่เรียกว่าพอยน์เตอร์สามารถเพิ่มลงในสแต็คที่เก็บที่อยู่หน่วยความจำสำหรับไบต์แรกของวัตถุและวัตถุนั้นอาศัยอยู่ในฮีป Heap นั้นค่อนข้างยุ่งเหยิงและค่อนข้างแพงในการบำรุงรักษาเพราะแตกต่างจากตัวแปร Stack พวกมันไม่กองซ้อนกันเป็นเส้นตรงและลงไปตามโปรแกรมที่เรียกใช้งาน พวกเขาสามารถมาและไปในลำดับไม่เฉพาะและพวกเขาสามารถเติบโตและหดตัว

การรับมือกับพอยน์เตอร์นั้นยาก สาเหตุของการรั่วไหลของหน่วยความจำบัฟเฟอร์โอเวอร์รันและความยุ่งยาก C # เพื่อช่วยเหลือ

ในระดับที่สูงขึ้น C # คุณไม่จำเป็นต้องคิดถึงพอยน์เตอร์ - กรอบงาน. Net (เขียนใน C ++) คิดเกี่ยวกับสิ่งเหล่านี้ให้คุณและนำเสนอให้คุณในรูปแบบ Reference to Objects และเพื่อประสิทธิภาพช่วยให้คุณเก็บค่าได้ง่ายขึ้น เช่น bools, bytes และ ints เป็นประเภทค่า ภายใต้ประทุนวัตถุและสิ่งต่าง ๆ ที่ยกระดับคลาสไปสู่ ​​Heap ที่มีราคาแพง Memory-Managed Heap ในขณะที่ Value Type จะไปใน Stack เดียวกันกับที่คุณมีใน C ระดับต่ำ - เร็วมาก

เพื่อประโยชน์ในการรักษาปฏิสัมพันธ์ระหว่าง 2 แนวคิดพื้นฐานที่แตกต่างกันของหน่วยความจำ (และกลยุทธ์สำหรับการจัดเก็บ) ง่าย ๆ จากมุมมองของโคเดอร์ Boxing ทำให้ค่าที่จะคัดลอกมาจากสแต็คใส่ในวัตถุและวางบนกอง - แพงกว่า แต่ปฏิสัมพันธ์ของเหลวกับโลกอ้างอิง ดังที่คำตอบอื่น ๆ ชี้ให้เห็นสิ่งนี้จะเกิดขึ้นเมื่อคุณพูดเช่น:

bool b = false; // Cheap, on Stack
object o = b; // Legal, easy to code, but complex - Boxing!
bool b2 = (bool)o; // Unboxing!

ภาพประกอบที่ดีเกี่ยวกับข้อได้เปรียบของ Boxing คือการตรวจสอบ null:

if (b == null) // Will not compile - bools can't be null
if (o == null) // Will compile and always return false

อ็อบเจกต์ o ของเราคือที่อยู่ทางเทคนิคในสแต็กที่ชี้ไปที่สำเนาของบูล bool ของเราซึ่งถูกคัดลอกไปที่ฮีป เราสามารถตรวจสอบ o เป็นโมฆะได้เพราะบูลนั้นถูกวางไว้แล้ว

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

รายการโครงสร้าง C # ในประวัติศาสตร์ที่ต้องใช้มวยซึ่งคุณควรหลีกเลี่ยง:

  • ระบบเหตุการณ์กลายเป็นสภาพการแข่งขันที่ไร้เดียงสาใช้และไม่สนับสนุน async เพิ่มในปัญหามวยและควรหลีกเลี่ยง (คุณสามารถแทนที่มันด้วยระบบเหตุการณ์ async ที่ใช้ Generics)

  • แบบจำลองเธรดและตัวจับเวลาแบบเก่าบังคับให้ใช้พารามิเตอร์กับกล่อง แต่ถูกแทนที่ด้วย async / คอยซึ่งมีความสะอาดกว่าและมีประสิทธิภาพมากกว่า

  • .Net 1.1 กลุ่มทั้งหมดอาศัยมวยเพราะพวกเขามาก่อน Generics เหล่านี้ยังคงเตะรอบใน System.Collections ในรหัสใหม่ใด ๆ ที่คุณควรใช้ Collections จาก System.Collections.Generic ซึ่งนอกเหนือจากการหลีกเลี่ยง Boxing ยังช่วยให้คุณปลอดภัยยิ่งขึ้นด้วย

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

ตามคำแนะนำของ Mikael ด้านล่าง:

ทำเช่นนี้

using System.Collections.Generic;

var employeeCount = 5;
var list = new List<int>(10);

ไม่ใช่สิ่งนี้

using System.Collections;

Int32 employeeCount = 5;
var list = new ArrayList(10);

ปรับปรุง

คำตอบนี้เดิมแนะนำให้ใช้ Int32, Bool และอื่น ๆ ทำให้เกิดการชกมวยเมื่อในความเป็นจริงพวกเขาเป็นนามแฝงง่ายสำหรับประเภทค่า นั่นคือ. Net มีประเภทเช่น Bool, Int32, String และ C # เป็นนามแฝงเป็น bool, int, string โดยไม่มีความแตกต่างในการทำงาน


4
คุณสอนฉันว่าโปรแกรมเมอร์หนึ่งร้อยคนและผู้เชี่ยวชาญด้านไอทีไม่สามารถอธิบายได้ในปีที่ผ่านมา แต่เปลี่ยนเป็นพูดในสิ่งที่คุณควรทำแทนสิ่งที่ต้องหลีกเลี่ยงเพราะมันยากที่จะทำตาม ... กฎพื้นฐานส่วนใหญ่มักจะไม่ไป 1 คุณจะไม่ทำสิ่งนี้แทนทำเช่นนี้
Mikael Puusaari

2
คำตอบนี้ควรถูกทำเครื่องหมายเป็นคำตอบร้อยครั้ง!
Pouyan

3
ไม่มี "Int" ใน c # มี int และ Int32 ฉันเชื่อว่าคุณผิดในการระบุหนึ่งคือประเภทค่าและอื่น ๆ เป็นประเภทอ้างอิงห่อประเภทค่า เว้นแต่ว่าฉันเข้าใจผิดนั่นเป็นความจริงใน Java แต่ไม่ใช่ C # ใน C # รายการที่แสดงสีน้ำเงินใน IDE เป็นชื่อแทนสำหรับคำจำกัดความของ struct ดังนั้น: int = Int32, bool = Boolean, string = String เหตุผลในการใช้บูลมากกว่าบูลีนเป็นเพราะแนะนำในแนวทางการออกแบบและแบบแผนของ MSDN มิฉะนั้นฉันรักคำตอบนี้ แต่ฉันจะลงคะแนนจนกว่าคุณจะพิสูจน์ว่าฉันผิดหรือแก้ไขในคำตอบของคุณ
Heriberto Lugo

2
หากคุณประกาศตัวแปรเป็น int และอีกตัวแปรหนึ่งเป็น Int32 หรือ bool และ Boolean - คลิกขวาและดูคำจำกัดความคุณจะสิ้นสุดในนิยามเดียวกันสำหรับ struct
Heriberto Lugo

2
@HeribertoLugo ถูกต้องบรรทัด "คุณควรหลีกเลี่ยงการประกาศประเภทค่าของคุณเป็น Bool แทนที่จะเป็นบูล" ผิดพลาด เมื่อ OP ชี้ให้เห็นคุณควรหลีกเลี่ยงการประกาศบูล (หรือบูลีนหรือประเภทค่าอื่น ๆ ) เป็นวัตถุ bool / Boolean, int / Int32 เป็นเพียงนามแฝงระหว่าง C # และ. NET: docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
STW

21

Boxing ไม่ใช่สิ่งที่คุณใช้จริงๆ - มันเป็นสิ่งที่ runtime ใช้เพื่อให้คุณสามารถจัดการกับชนิดของการอ้างอิงและค่าในแบบเดียวกันเมื่อจำเป็น ตัวอย่างเช่นถ้าคุณใช้ ArrayList เพื่อเก็บรายการจำนวนเต็มจำนวนเต็มจะได้รับการบรรจุให้พอดีกับสล็อตประเภทวัตถุใน ArrayList

ตอนนี้ใช้คอลเล็กชั่นทั่วไปหมดไปแล้ว หากคุณสร้าง a List<int>จะไม่มีการชกมวย - List<int>สามารถเก็บจำนวนเต็มได้โดยตรง


คุณยังต้องการการชกมวยสำหรับสิ่งต่าง ๆ เช่นการจัดรูปแบบสายอักขระประกอบ คุณอาจไม่เห็นมันบ่อยนักเมื่อใช้ยาชื่อสามัญ แต่ก็ยังมีอยู่อย่างแน่นอน
Jeremy S

1
จริง - มันแสดงตลอดเวลาใน ADO.NET เช่นกัน - ค่าพารามิเตอร์ sql เป็นวัตถุทั้งหมดไม่ว่าประเภทข้อมูลจริงคืออะไร
Ray

11

Boxing และ Unboxing ใช้เพื่อรักษาวัตถุประเภทค่าเป็นประเภทอ้างอิง ย้ายมูลค่าที่แท้จริงไปยังฮีปที่ได้รับการจัดการและเข้าถึงมูลค่าโดยอ้างอิง

หากไม่มีมวยและการแกะกล่องคุณจะไม่สามารถผ่านประเภทค่าโดยการอ้างอิง และนั่นหมายความว่าคุณไม่สามารถส่งค่าประเภทเป็นอินสแตนซ์ของ Object


ยังคงเป็นคำตอบที่ดีหลังจากเกือบ 10 ปีครับ +1
snr

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

8

สถานที่สุดท้ายที่ฉันต้อง unbox บางสิ่งบางอย่างคือเมื่อเขียนรหัสที่ดึงข้อมูลบางอย่างจากฐานข้อมูล (ฉันไม่ได้ใช้LINQ กับ SQLเพียงADO.NETเก่าธรรมดา):

int myIntValue = (int)reader["MyIntValue"];

โดยทั่วไปหากคุณทำงานกับ API ที่เก่ากว่าก่อนชื่อสามัญคุณจะพบกับมวย นอกเหนือจากนั้นมันไม่ธรรมดา


4

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

ฉันไม่คิดว่าเป็นจริงลองแทน:

class Program
    {
        static void Main(string[] args)
        {
            int x = 4;
            test(x);
        }

        static void test(object o)
        {
            Console.WriteLine(o.ToString());
        }
    }

มันใช้งานได้ดีฉันไม่ได้ใช้มวย / unboxing (ถ้าคอมไพเลอร์ทำอย่างนั้นไม่ได้อยู่เบื้องหลัง)


นั่นเป็นเพราะทุกอย่างสืบทอดมาจาก System.Object และคุณให้วิธีการวัตถุที่มีข้อมูลเพิ่มเติมดังนั้นโดยทั่วไปคุณจะเรียกวิธีการทดสอบกับสิ่งที่คาดหวังและสิ่งที่อาจคาดหวังเพราะมันไม่ได้คาดหวังอะไรโดยเฉพาะ หลายอย่างใน. NET นั้นทำเบื้องหลังและเหตุผลว่าทำไมมันจึงเป็นภาษาที่ใช้ง่ายมาก
Mikael Puusaari

1

ใน. net ทุกอินสแตนซ์ของ Object หรือชนิดใด ๆ ที่ได้มาจากนั้นรวมถึงโครงสร้างข้อมูลที่มีข้อมูลเกี่ยวกับประเภทของมัน ประเภทค่า "Real" ใน. net ไม่มีข้อมูลดังกล่าว ในการอนุญาตให้ข้อมูลในประเภทค่าถูกจัดการโดยรูทีนที่คาดว่าจะได้รับประเภทที่มาจากวัตถุระบบจะกำหนดประเภทค่าที่สอดคล้องกันให้กับแต่ละประเภทคลาสที่สอดคล้องกับสมาชิกและเขตข้อมูลเดียวกัน Boxing สร้างอินสแตนซ์ใหม่ของประเภทคลาสนี้คัดลอกฟิลด์จากอินสแตนซ์ประเภทค่า การแกะกล่องออกจะเป็นการคัดลอกฟิลด์จากอินสแตนซ์ของประเภทคลาสไปยังอินสแตนซ์ของประเภทค่า ประเภทคลาสทั้งหมดที่สร้างขึ้นจากประเภทค่ามาจากคลาส ValueType ที่มีชื่ออย่างไม่เจาะจง (ซึ่งถึงแม้ชื่อจะเป็นประเภทอ้างอิงจริง)


0

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

นี่เป็นจริงสำหรับวิธีการใด ๆ ที่ใช้objectเป็นพารามิเตอร์ - ซึ่งจะต้องเป็นประเภทการอ้างอิง


0

โดยทั่วไปแล้วคุณมักจะต้องการหลีกเลี่ยงการชกมวยประเภทค่าของคุณ

อย่างไรก็ตามมีเหตุการณ์ที่เกิดขึ้นได้ยากซึ่งสิ่งนี้มีประโยชน์ หากคุณต้องการกำหนดเป้าหมายเป็นเฟรมเวิร์ก 1.1 คุณจะไม่สามารถเข้าถึงคอลเล็กชันทั่วไปได้ การใช้คอลเลกชันใด ๆ ใน. NET 1.1 จะต้องดำเนินการกับประเภทค่าของคุณเป็น System.Object ซึ่งเป็นสาเหตุของการ Boxing / Unbox

ยังมีบางกรณีที่สิ่งนี้จะเป็นประโยชน์ใน. NET 2.0+ เมื่อใดก็ตามที่คุณต้องการใช้ประโยชน์จากความจริงที่ว่าทุกประเภทรวมถึงประเภทของมูลค่าสามารถได้รับการปฏิบัติเหมือนเป็นวัตถุโดยตรงคุณอาจต้องใช้ Boxing / unboxing บางครั้งสิ่งนี้มีประโยชน์เนื่องจากช่วยให้คุณสามารถบันทึกประเภทใดก็ได้ในคอลเล็กชัน (โดยใช้วัตถุแทน T ในคอลเล็กชันทั่วไป) แต่โดยทั่วไปจะดีกว่าเพื่อหลีกเลี่ยงสิ่งนี้เนื่องจากคุณสูญเสียความปลอดภัยประเภท ในบางกรณีที่การชกมวยเกิดขึ้นบ่อยครั้งคือเมื่อคุณใช้ Reflection การโทรที่ต้องไตร่ตรองจะต้องใช้การชกมวย / ไม่ทำกล่องเมื่อทำงานกับประเภทค่าเนื่องจากไม่ทราบชนิดล่วงหน้า


0

Boxing คือการแปลงค่าเป็นประเภทการอ้างอิงพร้อมกับข้อมูลที่ออฟเซ็ตบางอย่างในวัตถุบนฮีป

สำหรับสิ่งที่มวยทำจริง นี่คือตัวอย่างบางส่วน

โมโน C ++

void* mono_object_unbox (MonoObject *obj)
 {    
MONO_EXTERNAL_ONLY_GC_UNSAFE (void*, mono_object_unbox_internal (obj));
 }

#define MONO_EXTERNAL_ONLY_GC_UNSAFE(t, expr) \
    t result;       \
    MONO_ENTER_GC_UNSAFE;   \
    result = expr;      \
    MONO_EXIT_GC_UNSAFE;    \
    return result;

static inline gpointer
mono_object_get_data (MonoObject *o)
{
    return (guint8*)o + MONO_ABI_SIZEOF (MonoObject);
}

#define MONO_ABI_SIZEOF(type) (MONO_STRUCT_SIZE (type))
#define MONO_STRUCT_SIZE(struct) MONO_SIZEOF_ ## struct
#define MONO_SIZEOF_MonoObject (2 * MONO_SIZEOF_gpointer)

typedef struct {
    MonoVTable *vtable;
    MonoThreadsSync *synchronisation;
} MonoObject;

Unboxing in Mono เป็นกระบวนการชี้พอยน์เตอร์ที่ออฟเซ็ต 2 gpointers ในวัตถุ (เช่น 16 ไบต์) เป็นgpointer void*สิ่งนี้สมเหตุสมผลเมื่อดูที่คำจำกัดความMonoObjectเนื่องจากเป็นเพียงส่วนหัวของข้อมูลอย่างชัดเจน

C ++

ในการใส่ค่าใน C ++ คุณสามารถทำสิ่งต่อไปนี้:

#include <iostream>
#define Object void*

template<class T> Object box(T j){
  return new T(j);
}

template<class T> T unbox(Object j){
  T temp = *(T*)j;
  delete j;
  return temp;
}

int main() {
  int j=2;
  Object o = box(j);
  int k = unbox<int>(o);
  std::cout << k;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.