ใน C # เหตุใด String จึงเป็นประเภทการอ้างอิงที่ทำงานเหมือนประเภทค่า


371

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

เหตุใดสตริงจึงไม่เป็นเพียงประเภทค่า


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

BTW นี้เป็นคำตอบเดียวกันสำหรับ C ++ (แม้ว่าความแตกต่างระหว่างค่าและประเภทการอ้างอิงไม่ชัดเจนในภาษา) การตัดสินใจที่จะstd::stringทำตัวเหมือนคอลเลกชันเป็นข้อผิดพลาดเก่าที่ไม่สามารถแก้ไขได้ในขณะนี้
Elazar

คำตอบ:


333

สตริงไม่ใช่ประเภทค่าเนื่องจากมีขนาดใหญ่และจำเป็นต้องเก็บไว้ในกอง ชนิดของค่าคือ (ในการนำไปใช้งานทั้งหมดของ CLR ณ ขณะนั้น) เก็บไว้ในสแต็ก สแต็กการจัดสรรสตริงจะทำลายทุกสิ่ง: สแต็คมีเพียง 1MB สำหรับ 32- บิตและ 4MB สำหรับ 64- บิต, คุณจะต้องทำกล่องแต่ละสตริง, เกิดการลงโทษการคัดลอก, คุณไม่สามารถฝึกงานและใช้หน่วยความจำ บอลลูนจะ ฯลฯ

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


75
ฉันไปที่นี่ แต่เพียงเพราะเปิดโอกาสให้ฉันเชื่อมโยงไปยังโพสต์บล็อกที่เกี่ยวข้องกับคำถาม: ประเภทค่าไม่จำเป็นต้องเก็บไว้ในกองซ้อน มันมักจะเป็นจริงใน ms.net แต่ไม่ได้ระบุไว้โดยข้อกำหนด CLI ความแตกต่างที่สำคัญระหว่างค่าและประเภทการอ้างอิงคือประเภทการอ้างอิงนั้นเป็นไปตามความหมายของการคัดลอกค่า ดูblogs.msdn.com/ericlippert/archive/2009/04/27/…และblogs.msdn.com/ericlippert/archive/2009/05/04/…
เบ็นชเวนน์

8
@Qwertie: Stringไม่ใช่ขนาดตัวแปร เมื่อคุณเพิ่มเข้าไปคุณกำลังสร้างStringวัตถุอื่นโดยจัดสรรหน่วยความจำใหม่สำหรับวัตถุนั้น
codekaizen

5
ตามที่กล่าวไว้ในทางทฤษฎีแล้วสตริงสามารถเป็นประเภทค่า (struct) แต่ "ค่า" จะไม่มีอะไรมากไปกว่าการอ้างอิงถึงสตริง ผู้ออกแบบ. NET ตัดสินใจตัดพ่อค้าคนกลางโดยธรรมชาติ (การจัดการโครงสร้างไม่มีประสิทธิภาพใน. NET 1.0 และเป็นเรื่องธรรมดาที่จะติดตาม Java ซึ่งสตริงได้ถูกกำหนดไว้แล้วเป็นข้อมูลอ้างอิงแทนที่จะเป็นประเภทดั้งเดิมบวกกับถ้าเป็นสตริง ชนิดของค่าจากนั้นการแปลงให้เป็นวัตถุจะต้องให้มันถูกบรรจุในกล่องซึ่งเป็นความไร้ประสิทธิภาพที่ไม่จำเป็น)
Qwertie

7
@codekaizen Qwertie พูดถูก แต่ฉันคิดว่าถ้อยคำสับสน สตริงหนึ่งอาจมีขนาดแตกต่างจากสตริงอื่นดังนั้นจึงไม่เหมือนกับชนิดของค่าที่แท้จริงคอมไพเลอร์ไม่สามารถทราบล่วงหน้าได้ว่าจะจัดสรรพื้นที่จำนวนเท่าใดเพื่อเก็บค่าสตริง ตัวอย่างเช่นการInt32มี 4 ไบต์เสมอดังนั้นคอมไพเลอร์จัดสรร 4 ไบต์ทุกครั้งที่คุณกำหนดตัวแปรสตริง คอมไพเลอร์ควรจัดสรรหน่วยความจำเท่าใดเมื่อพบintตัวแปร (ถ้าเป็นประเภทค่า) เข้าใจว่ายังไม่ได้กำหนดค่าในขณะนั้น
Kevin Brock

2
ขออภัยพิมพ์ผิดในความคิดเห็นของฉันที่ฉันไม่สามารถแก้ไขได้ในขณะนี้ ที่ควรเป็น .... ตัวอย่างเช่น, มีค่าInt32เป็น 4 ไบต์เสมอดังนั้นคอมไพเลอร์จะจัดสรร 4 ไบต์ทุกครั้งที่คุณกำหนดintตัวแปร คอมไพเลอร์ควรจัดสรรหน่วยความจำเท่าใดเมื่อพบstringตัวแปร (ถ้าเป็นประเภทค่า) เข้าใจว่ายังไม่ได้กำหนดค่าในขณะนั้น
Kevin Brock

57

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

มันมีความหมายที่คุ้มค่าที่จะทำให้โลกมีสติ คุณลองนึกภาพดูว่ามันยากแค่ไหนที่จะใช้รหัสถ้า

string s = "hello";
string t = "hello";
bool b = (s == t);

ตั้งbให้เป็นfalse? ลองนึกภาพว่าการเขียนโค้ดเกี่ยวกับแอพพลิเคชันใดทำได้ยาก


44
Java ไม่เป็นที่รู้จักสำหรับการแหลมคม
jason

3
@ แมท: แน่นอน เมื่อฉันเปลี่ยนไปใช้ C # นี่เป็นความสับสนเพราะฉันมักจะใช้ (ทำบางครั้ง). equals (.. ) เพื่อเปรียบเทียบสตริงในขณะที่เพื่อนร่วมทีมของฉันเพิ่งใช้ "==" ฉันไม่เคยเข้าใจว่าทำไมพวกเขาถึงไม่ปล่อย "==" เพื่อเปรียบเทียบการอ้างอิงถึงแม้ว่าคุณคิดว่า 90% ของเวลาคุณอาจต้องการเปรียบเทียบเนื้อหาไม่ใช่การอ้างอิงสำหรับสตริง
Juri

7
@Juri: จริง ๆ แล้วฉันคิดว่ามันไม่เป็นที่พึงปรารถนาที่จะตรวจสอบการอ้างอิงเนื่องจากบางครั้งnew String("foo");และอื่น ๆnew String("foo")สามารถประเมินในการอ้างอิงเดียวกันซึ่งไม่ใช่สิ่งที่คุณคาดหวังว่าnewผู้ประกอบการจะทำ (หรือคุณสามารถบอกกรณีที่ฉันต้องการเปรียบเทียบการอ้างอิงได้หรือไม่)
Michael

1
@Michael เอาล่ะคุณต้องรวมการเปรียบเทียบการอ้างอิงในการเปรียบเทียบทั้งหมดเพื่อให้ได้การเปรียบเทียบกับ null อีกสถานที่ที่ดีในการเปรียบเทียบการอ้างอิงกับสตริงคือเมื่อเปรียบเทียบมากกว่าการเปรียบเทียบความเท่าเทียมกัน สองสตริงที่เทียบเท่ากันเมื่อเปรียบเทียบควรส่งคืน 0 การตรวจสอบกรณีนี้ใช้เวลานานเท่ากับการเปรียบเทียบทั้งหมดดังนั้นจึงไม่ใช่ทางลัดที่มีประโยชน์ การตรวจสอบReferenceEquals(x, y)คือการทดสอบที่รวดเร็วและคุณสามารถส่งคืนได้ทันที 0 และเมื่อผสมกับการทดสอบแบบ null ของคุณจะไม่เพิ่มงานอีกต่อไป
จอนฮันนา

1
... การมีสตริงเป็นประเภทค่าของสไตล์นั้นแทนที่จะเป็นประเภทคลาสจะหมายถึงค่าเริ่มต้นของ a stringอาจทำงานเป็นสตริงว่างเปล่า (เหมือนในระบบ pre-.net) แทนการอ้างอิงแบบ null ที่จริงแล้วการตั้งค่าของฉันเองจะมีประเภทค่าStringที่มีการอ้างอิงประเภทNullableStringกับอดีตมีค่าเริ่มต้นเทียบเท่าString.Emptyและหลังมีค่าเริ่มต้นnullและมีกฎพิเศษมวย / unboxing (เช่นมวยที่เริ่มต้น - มูลค่าNullableStringจะให้การอ้างอิงถึงString.Empty)
supercat

26

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

คำถามของความผันแปรเป็นปัญหาที่แยกจากกัน ทั้งประเภทการอ้างอิงและประเภทค่าสามารถเปลี่ยนแปลงได้หรือไม่เปลี่ยนรูป โดยทั่วไปประเภทค่าจะไม่เปลี่ยนรูปแม้ว่าเนื่องจากความหมายสำหรับประเภทค่าที่ไม่แน่นอนอาจทำให้เกิดความสับสน

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

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

พูดมากขึ้นโดยทั่วไป, Strings มีสิ่งที่เรียกว่าความหมายค่า นี่เป็นแนวคิดทั่วไปมากกว่าประเภทของค่าซึ่งเป็นรายละเอียดการใช้งานเฉพาะ C # ชนิดของค่ามีความหมายของค่า แต่ชนิดของการอ้างอิงอาจมีความหมายของค่า เมื่อชนิดมีค่าซีแมนทิกส์คุณจะไม่สามารถบอกได้ว่าการใช้งานพื้นฐานนั้นเป็นประเภทอ้างอิงหรือประเภทของค่าหรือไม่ดังนั้นคุณสามารถพิจารณารายละเอียดการใช้งานนั้นได้


ความแตกต่างระหว่างประเภทค่าและประเภทการอ้างอิงไม่ได้เกี่ยวกับประสิทธิภาพเลย มันเกี่ยวกับว่าตัวแปรมีวัตถุจริงหรือการอ้างอิงถึงวัตถุ สตริงอาจไม่เป็นประเภทค่าเนื่องจากขนาดของสตริงเป็นตัวแปร มันจะต้องมีค่าคงที่เพื่อเป็นประเภทค่า ประสิทธิภาพแทบไม่มีอะไรเกี่ยวข้องกับมันเลย ประเภทการอ้างอิงก็ไม่แพงที่จะสร้างเลย
Servy

2
@Sevy: ขนาดของสตริงเป็นค่าคงที่
JacquesB

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

1
@Sevy: ขนาดของอาร์เรย์คงที่
JacquesB

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

16

นี่เป็นคำตอบที่ล่าช้าสำหรับคำถามเก่า แต่คำตอบอื่น ๆ ทั้งหมดหายไปซึ่งเป็นที่. NET ไม่ได้มีชื่อสามัญจนกระทั่ง. NET 2.0 ในปี 2005

Stringเป็นประเภทการอ้างอิงแทนที่จะเป็นประเภทค่าเนื่องจากมีความสำคัญอย่างยิ่งสำหรับ Microsoft เพื่อให้แน่ใจว่าสามารถเก็บสตริงได้อย่างมีประสิทธิภาพที่สุดในคอลเลกชันที่ไม่ใช่แบบทั่วไปเช่นSystem.Collections.ArrayListเช่น

การเก็บค่าประเภทในคอลเลกชันที่ไม่ใช่แบบทั่วไปต้องมีการแปลงแบบพิเศษเป็นประเภทobjectที่เรียกว่ามวย เมื่อ CLR ทำกล่องชนิดของค่ามันจะตัดค่าภายในSystem.Objectและเก็บไว้ในฮีปที่ได้รับการจัดการ

การอ่านค่าจากคอลเล็กชันต้องการการดำเนินการผกผันซึ่งเรียกว่า unboxing

ทั้งการชกมวยและการไม่จิกมีค่าใช้จ่ายที่ไม่สำคัญ: มวยต้องการการจัดสรรเพิ่มเติม, การยกเลิกการจั่วต้องมีการตรวจสอบประเภท

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

หากมีข้อมูลทั่วไปตั้งแต่วันแรกฉันเดาว่าการใช้สตริงเป็นประเภทค่าอาจเป็นวิธีที่ดีกว่าด้วยความหมายที่ง่ายกว่าการใช้หน่วยความจำที่ดีขึ้นและตำแหน่งแคชที่ดีขึ้น List<string>ที่มีเพียงสายขนาดเล็กจะได้รับบล็อกติดกันเดียวของหน่วยความจำ


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

8

สตริงไม่เพียง แต่เป็นประเภทการอ้างอิงที่ไม่เปลี่ยนรูป ผู้ได้รับมอบหมาย Multi-cast เกินไป นี่คือเหตุผลว่าทำไมจึงปลอดภัยที่จะเขียน

protected void OnMyEventHandler()
{
     delegate handler = this.MyEventHandler;
     if (null != handler)
     {
        handler(this, new EventArgs());
     }
}

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

string s1 = "my string";
//some code here
string s2 = "my string";

โอกาสที่ทั้งสองอินสแตนซ์ของค่าคงที่ "สตริงของฉัน" จะถูกจัดสรรในแอสเซมบลีของคุณเพียงครั้งเดียว

หากคุณต้องการจัดการสตริงเหมือนประเภทอ้างอิงปกติให้ใส่สตริงไว้ใน StringBuilder ใหม่ (สตริง s) หรือใช้ MemoryStreams

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


1
มีตัวอย่างมากมายของประเภทการอ้างอิงที่ไม่เปลี่ยนรูป และอีกตัวอย่างสตริงที่แน่นอนรับประกันภายใต้การใช้งานในปัจจุบันสวยมาก - ในทางเทคนิคมันเป็นเป็นต่อโมดูล (ไม่ได้ต่อการประกอบ) - แต่ที่มักจะเป็นสิ่งเดียวกัน ...
Marc Gravell

5
จุดสุดท้าย: StringBuilder ไม่ช่วยถ้าคุณพยายามส่งสตริงขนาดใหญ่ (เนื่องจากมีการใช้งานจริงเป็นสตริงอยู่แล้ว) - StringBuilder มีประโยชน์ในการจัดการสตริงหลายครั้ง
Marc Gravell

คุณหมายถึงผู้แทนผู้มอบหมายใช่หรือไม่? (ขออภัยที่จะจู้จี้จุกจิก .. แต่มันใกล้กับนามสกุล (ไม่ธรรมดา) ฉันรู้ .... )
Pure.Krome

6

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

บางที Jon Skeet อาจช่วยได้ที่นี่?


5

มันเป็นเรื่องของประสิทธิภาพเป็นหลัก

การมีสตริงทำงานชนิดของค่า LIKE ช่วยให้เมื่อเขียนโค้ด แต่ถ้าเป็นประเภทของค่าจะทำให้การทำงานมีประสิทธิภาพอย่างมาก

เพื่อการมองในเชิงลึกลองดูบทความดีๆเกี่ยวกับสายอักขระในกรอบงาน. net


3

กล่าวง่ายๆว่าค่าใด ๆ ที่มีขนาดแน่นอนสามารถถือว่าเป็นประเภทค่าได้


นี่ควรเป็นความเห็น
Kяσѕρєя K

ง่ายต่อการเข้าใจสำหรับ ppl ใหม่ถึง c #
ยาว

2

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


มันเป็นประเภทอ้างอิง (ฉันเชื่อ) เพราะมันไม่ได้มาจาก System.ValueType จาก MSDN ข้อสังเกตบน System.ValueType: ชนิดข้อมูลจะถูกแยกออกเป็นประเภทค่าและประเภทการอ้างอิง ประเภทค่าเป็นแบบจัดสรรสแต็กหรือจัดสรรแบบอินไลน์ในโครงสร้าง ประเภทการอ้างอิงได้รับการจัดสรรฮีป
Davy8

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

เสื้อคลุมถูกทำเครื่องหมายเพื่อให้ระบบรู้ว่ามันมีประเภทค่า กระบวนการนี้เป็นที่รู้จักกันในชื่อมวยและกระบวนการย้อนกลับเป็นที่รู้จักในฐานะ unboxing Boxing และ unboxing อนุญาตให้ใช้ชนิดใดก็ได้ในฐานะวัตถุ (ในเว็บไซต์หลัง, อาจจะได้เชื่อมโยงเพียงเพื่อให้บทความ.)
Davy8

2

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

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

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


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

5
@WebMatrix, @ Davy8: ประเภทดั้งเดิม (int, double, bool, ... ) นั้นไม่เปลี่ยนรูป
jason

1
@ Jason ฉันคิดว่าคำที่ไม่เปลี่ยนรูปส่วนใหญ่ใช้กับวัตถุ (ประเภทการอ้างอิง) ซึ่งไม่สามารถเปลี่ยนแปลงได้หลังจากการเริ่มต้นเช่นสตริงเมื่อมีการเปลี่ยนแปลงค่าสตริงภายในอินสแตนซ์ใหม่ของสตริงถูกสร้างขึ้นและวัตถุต้นฉบับยังคงไม่เปลี่ยนแปลง สิ่งนี้มีผลกับประเภทของค่าอย่างไร
WebMatrix

8
อย่างไรก็ตามใน "int n = 4; n = 9;" ไม่ใช่ตัวแปร int ของคุณที่เป็น "ไม่เปลี่ยนรูป" ในแง่ของ "ค่าคงที่"; มันคือค่า 4 นั้นไม่เปลี่ยนรูปก็ไม่เปลี่ยนเป็น 9 ตัวแปร int ของคุณ "n" ก่อนมีค่า 4 จากนั้นเป็นค่าที่แตกต่างกัน 9; แต่คุณค่าเหล่านั้นไม่อาจเปลี่ยนแปลงได้ ตรงไปตรงมาสำหรับฉันนี้อยู่ใกล้กับ WTF
Daniel Daranas

1
+1 ฉันเบื่อที่จะได้ยิน "สตริงเป็นเหมือนประเภทค่า" เมื่อพวกเขาไม่ได้เป็น
Jon Hanna

1

ไม่ง่ายอย่างที่ Strings ประกอบไปด้วยตัวละคร ฉันดูสตริงเป็นอาร์เรย์อักขระ [] ดังนั้นจึงอยู่ในฮีปเนื่องจากตำแหน่งหน่วยความจำอ้างอิงเก็บอยู่ในสแต็กและชี้ไปที่ตำแหน่งเริ่มต้นของตำแหน่งหน่วยความจำของอาเรย์บนฮีป ไม่ทราบขนาดสตริงก่อนที่จะจัดสรร ... สมบูรณ์แบบสำหรับฮีป

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


1
"ไม่ทราบขนาดสตริงก่อนจัดสรร" - สิ่งนี้ไม่ถูกต้องใน CLR
codekaizen

-1

ความเสี่ยงในการได้รับการลงคะแนนเสียงลึกลับอีกครั้ง ... ความจริงที่ว่าหลายคนพูดถึงสแต็คและหน่วยความจำเกี่ยวกับประเภทของค่าและประเภทดั้งเดิมคือเพราะพวกเขาจะต้องลงทะเบียนในไมโครโปรเซสเซอร์ คุณไม่สามารถผลักดันหรือป๊อปอัพบางสิ่งไปยัง / จากสแต็กหากใช้บิตมากกว่าการลงทะเบียนมี .... คำแนะนำคือตัวอย่างเช่น "pop eax" - เนื่องจาก eax นั้นกว้าง 32 บิตบนระบบ 32 บิต

ประเภทดั้งเดิมจุดลอยตัวได้รับการจัดการโดย FPU ซึ่งมีความกว้าง 80 บิต

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

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