ฉันกำลังมองหาคำตอบที่ชัดเจนกระชับและถูกต้อง
ตามหลักการแล้วเป็นคำตอบที่แท้จริงแม้ว่าจะยินดีต้อนรับลิงก์ไปยังคำอธิบายที่ดี
ฉันกำลังมองหาคำตอบที่ชัดเจนกระชับและถูกต้อง
ตามหลักการแล้วเป็นคำตอบที่แท้จริงแม้ว่าจะยินดีต้อนรับลิงก์ไปยังคำอธิบายที่ดี
language-but-not-type-agnostic
? static-language-agnostic
เหรอ? ฉันไม่แน่ใจว่า SO ต้องการความแตกต่าง อาจเป็นคำถามที่ดีสำหรับเมตาว่า
คำตอบ:
ค่าแบบบรรจุกล่องคือโครงสร้างข้อมูลที่มีการห่อตัวน้อยที่สุดสำหรับประเภทดั้งเดิม * โดยทั่วไปค่าแบบบรรจุกล่องจะถูกเก็บไว้เป็นตัวชี้ไปยังวัตถุบนฮีปกอง
ดังนั้นค่าแบบบรรจุกล่องจะใช้หน่วยความจำมากกว่าและใช้การค้นหาหน่วยความจำอย่างน้อยสองรายการเพื่อเข้าถึง: หนึ่งครั้งเพื่อรับตัวชี้และอีกค่าหนึ่งเพื่อติดตามตัวชี้นั้นไปยังแบบดั้งเดิม เห็นได้ชัดว่านี่ไม่ใช่สิ่งที่คุณต้องการในวงในของคุณ ในทางกลับกันค่าแบบบรรจุกล่องมักจะเล่นได้ดีกว่ากับประเภทอื่น ๆ ในระบบ เนื่องจากเป็นโครงสร้างข้อมูลชั้นหนึ่งในภาษาจึงมีข้อมูลเมตาและโครงสร้างที่คาดหวังซึ่งโครงสร้างข้อมูลอื่น ๆ มี
ในคอลเลกชันทั่วไปของ Java และ Haskell ต้องไม่มีค่าที่ไม่มีกล่อง คอลเลกชันทั่วไปใน. NET สามารถเก็บค่าที่ไม่มีกล่องโดยไม่มีบทลงโทษ โดยที่ข้อมูลทั่วไปของ Java ใช้สำหรับการตรวจสอบประเภทเวลาคอมไพล์เท่านั้น. NET จะสร้างคลาสเฉพาะสำหรับแต่ละประเภททั่วไปที่สร้างอินสแตนซ์ในขณะรันไทม์ในเวลาทำงาน
Java และ Haskell มีอาร์เรย์ที่ไม่มีกล่อง แต่มีความสะดวกน้อยกว่าคอลเล็กชันอื่น ๆ อย่างเห็นได้ชัด อย่างไรก็ตามเมื่อต้องการประสิทธิภาพสูงสุดก็คุ้มค่ากับความไม่สะดวกเล็กน้อยที่จะหลีกเลี่ยงค่าใช้จ่ายในการชกมวยและการแกะกล่อง
* สำหรับการสนทนานี้ค่าดั้งเดิมคือค่าใดก็ได้ที่สามารถเก็บไว้ได้ call stackแทนที่จะเก็บไว้เป็นตัวชี้ไปยังค่าบนฮีป บ่อยครั้งที่เป็นเพียงประเภทของเครื่องจักร (ints, float, ฯลฯ ) โครงสร้างและอาร์เรย์ขนาดคงที่ในบางครั้ง .NET-land เรียกพวกเขาว่าประเภทมูลค่า (ตรงข้ามกับประเภทอ้างอิง) ชาว Java เรียกพวกเขาว่าประเภทดั้งเดิม Haskellions เรียกพวกเขาว่าไม่มีกล่อง
** ฉันยังเน้นที่ Java, Haskell และ C # ในคำตอบนี้เพราะนั่นคือสิ่งที่ฉันรู้ สำหรับสิ่งที่คุ้มค่า Python, Ruby และ Javascript ล้วนมีค่าแบบบรรจุกล่องโดยเฉพาะ วิธีนี้เรียกอีกอย่างว่าแนวทาง "ทุกสิ่งคือวัตถุ" ***
*** ข้อแม้: ในบางกรณีคอมไพเลอร์ / JIT ขั้นสูงสามารถตรวจพบได้จริงว่าค่าที่อยู่ในกล่องตามความหมายเมื่อดูต้นทางสามารถเป็นค่าที่ไม่มีกล่องในขณะรันไทม์ได้อย่างปลอดภัย โดยพื้นฐานแล้วต้องขอบคุณผู้ใช้ภาษาที่ยอดเยี่ยมบางครั้งกล่องของคุณก็ฟรี
จากC # 3.0 โดยย่อ :
การชกมวยเป็นการแสดงประเภทค่าเป็นประเภทอ้างอิง:
int x = 9;
object o = x; // boxing the int
การแกะกล่องคือ ... สิ่งที่ตรงกันข้าม:
// unboxing o
object o = 9;
int x = (int)o;
Boxing & unboxing คือกระบวนการแปลงค่าดั้งเดิมเป็นคลาส Wrapper เชิงวัตถุ (การชกมวย) หรือการแปลงค่าจากคลาส Wrapper เชิงวัตถุกลับไปเป็นค่าดั้งเดิม (unboxing)
ตัวอย่างเช่นใน java คุณอาจต้องแปลงint
ค่าเป็นInteger
(boxing) หากคุณต้องการจัดเก็บในรูปแบบCollection
เนื่องจากไม่สามารถจัดเก็บแบบดั้งเดิมในCollection
ออบเจ็กต์ได้ แต่เมื่อคุณต้องการนำมันกลับมาCollection
คุณอาจต้องการรับค่าเป็นint
และไม่ใช่Integer
ดังนั้นคุณจึงต้องแกะกล่องออก
การชกมวยและการแกะกล่องนั้นไม่ได้เลวร้ายแต่อย่างใด แต่เป็นการแลกเปลี่ยน ขึ้นอยู่กับการใช้งานภาษาอาจช้าลงและใช้หน่วยความจำมากขึ้นกว่าการใช้แบบดั้งเดิม อย่างไรก็ตามอาจช่วยให้คุณใช้โครงสร้างข้อมูลระดับที่สูงขึ้นและมีความยืดหยุ่นมากขึ้นในโค้ดของคุณ
ทุกวันนี้มีการพูดถึงกันมากที่สุดในบริบทของคุณลักษณะ "autoboxing / autounboxing" ของ Java (และภาษาอื่น ๆ ) นี่คือJava คำอธิบายที่เป็นศูนย์กลางของ autoboxing
ใน. Net:
บ่อยครั้งที่คุณไม่สามารถพึ่งพาประเภทของตัวแปรที่ฟังก์ชันจะใช้ดังนั้นคุณต้องใช้ตัวแปรออบเจ็กต์ที่ขยายจากตัวหารร่วมที่ต่ำที่สุด - ใน. Net นี่คือ object
ในสุทธินี้จะ
อย่างไรก็ตามobject
เป็นคลาสและเก็บเนื้อหาไว้เป็นข้อมูลอ้างอิง
List<int> notBoxed = new List<int> { 1, 2, 3 };
int i = notBoxed[1]; // this is the actual value
List<object> boxed = new List<object> { 1, 2, 3 };
int j = (int) boxed[1]; // this is an object that can be 'unboxed' to an int
แม้ว่าทั้งสองรายการจะมีข้อมูลเดียวกัน แต่รายการที่สองจะมีขนาดใหญ่และช้ากว่า แต่ละค่าในรายการที่สองเป็นการอ้างอิงถึงobject
ที่เก็บไฟล์int
.
สิ่งนี้เรียกว่าบรรจุกล่องเนื่องจากint
ถูกห่อด้วยobject
. เมื่อมันกลับมาint
จะไม่มีกล่อง - แปลงกลับเป็นค่าของมัน
สำหรับประเภทค่า (เช่นทั้งหมด structs
) สิ่งนี้ช้าและอาจใช้พื้นที่มากขึ้น
สำหรับประเภทการอ้างอิง (เช่น all classes
) นี่เป็นปัญหาน้อยกว่ามากเนื่องจากจะถูกเก็บไว้เป็นข้อมูลอ้างอิงอยู่ดี
ปัญหาเพิ่มเติมเกี่ยวกับประเภทค่าแบบบรรจุกล่องคือไม่ชัดเจนว่าคุณกำลังจัดการกับกล่องแทนที่จะเป็นค่า เมื่อคุณเปรียบเทียบสองค่าstructs
แล้วคุณกำลังเปรียบเทียบค่า แต่เมื่อคุณเปรียบเทียบสองค่าclasses
แล้ว (โดยค่าเริ่มต้น) คุณกำลังเปรียบเทียบข้อมูลอ้างอิงนั่นคืออินสแตนซ์เดียวกันหรือไม่
สิ่งนี้อาจสร้างความสับสนเมื่อจัดการกับประเภทมูลค่าบรรจุกล่อง:
int a = 7;
int b = 7;
if(a == b) // Evaluates to true, because a and b have the same value
object c = (object) 7;
object d = (object) 7;
if(c == d) // Evaluates to false, because c and d are different instances
มันง่ายที่จะแก้ไข:
if(c.Equals(d)) // Evaluates to true because it calls the underlying int's equals
if(((int) c) == ((int) d)) // Evaluates to true once the values are cast
อย่างไรก็ตามมันเป็นอีกสิ่งหนึ่งที่ต้องระวังเมื่อจัดการกับค่ากล่อง
Object
ไม่ได้ใช้ตัวดำเนินการความเท่าเทียมกัน แต่สามารถเปรียบเทียบประเภทคลาสกับตัวIs
ดำเนินการได้ ตรงกันข้ามInt32
สามารถนำมาใช้กับผู้ประกอบการเท่าเทียมกัน Is
แต่ไม่ได้ ความแตกต่างนั้นทำให้ชัดเจนขึ้นมากว่ากำลังทำการเปรียบเทียบประเภทใด
Boxing
คือขั้นตอนการแปลงประเภทมูลค่าเป็นประเภทอ้างอิง ในขณะที่Unboxing
การแปลงประเภทการอ้างอิงเป็นประเภทค่า
EX: int i = 123;
object o = i;// Boxing
int j = (int)o;// UnBoxing
ชนิดราคา ได้แก่ : int
, char
และ,structures
enumerations
ประเภทอ้างอิง:
Classes
, interfaces
, arrays
, strings
และobjects
คอลเลกชันทั่วไป. NET FCL:
List<T>
Dictionary<TKey, UValue>
SortedDictionary<TKey, UValue>
Stack<T>
Queue<T>
LinkedList<T>
ได้รับการออกแบบมาเพื่อเอาชนะปัญหาด้านประสิทธิภาพของการชกมวยและการแกะกล่องในการใช้งานคอลเลกชันก่อน
สำหรับข้อมูลเพิ่มเติมโปรดดูบทที่ 16, CLR ผ่าน C # (ฉบับที่ 2)
การชกมวยและการแกะกล่องช่วยอำนวยความสะดวกให้กับประเภทมูลค่าที่จะถือว่าเป็นวัตถุ Boxing หมายถึงการแปลงค่าเป็นอินสแตนซ์ของประเภทการอ้างอิงอ็อบเจ็กต์ ตัวอย่างเช่นInt
เป็นคลาสและint
เป็นชนิดข้อมูล แปลงint
ไปInt
เป็นยกตัวอย่างของมวยในขณะที่แปลงInt
ไปint
เป็น unboxing แนวคิดนี้ช่วยในการรวบรวมขยะ Unboxing ในทางกลับกันแปลงประเภทวัตถุเป็นประเภทค่า
int i=123;
object o=(object)i; //Boxing
o=123;
i=(int)o; //Unboxing.
var ii = 123; typeof ii
ผลตอบแทนที่ ผลตอบแทน นี่คือจาวาสคริปต์ที่เทียบเท่ากับการชกมวย ค่า iiObj จะถูกแปลงเป็นตัวเลขดั้งเดิมโดยอัตโนมัติ (ไม่มีกล่อง) เพื่อคำนวณเลขคณิตและส่งคืนค่าที่ไม่มีกล่อง number
var iiObj = new Number(123); typeof iiObj
object
typeof ii + iiObj
number
เช่นเดียวกับสิ่งอื่น ๆ การทำกล่องอัตโนมัติอาจเป็นปัญหาได้หากไม่ใช้อย่างระมัดระวัง คลาสสิกคือการลงท้ายด้วย NullPointerException และไม่สามารถติดตามได้ แม้จะมีดีบักเกอร์ ลองสิ่งนี้:
public class TestAutoboxNPE
{
public static void main(String[] args)
{
Integer i = null;
// .. do some other stuff and forget to initialise i
i = addOne(i); // Whoa! NPE!
}
public static int addOne(int i)
{
return i + 1;
}
}
i
เริ่มต้นก่อนกำหนด ทำให้เป็นการประกาศว่าง ( Integer i;
) เพื่อให้คอมไพลเลอร์สามารถชี้ให้เห็นว่าคุณลืมเริ่มต้นหรือรอเพื่อประกาศจนกว่าคุณจะทราบค่าของมัน