อะไรคือความแตกต่างระหว่าง struct และ class ใน. NET?


คำตอบ:


1058

ใน .NET มีสองประเภทประเภทประเภทของการอ้างอิงและประเภทค่า

structs เป็นประเภทค่าและการเรียนเป็นประเภทของการอ้างอิง

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

ตัวแปรที่มีประเภทค่าประกอบด้วยค่าชนิดทั้งหมด สำหรับ struct หมายความว่าตัวแปรมีโครงสร้างทั้งหมดพร้อมฟิลด์ทั้งหมด

ตัวแปรที่มีประเภทการอ้างอิงประกอบด้วยตัวชี้หรือการอ้างอิงไปยังที่อื่นในหน่วยความจำที่มีค่าจริงอยู่

สิ่งนี้มีประโยชน์อย่างหนึ่งเริ่มต้นด้วย:

  • ประเภทค่าจะมีค่าอยู่เสมอ
  • ประเภทการอ้างอิงสามารถมีการอ้างอิงที่เป็นโมฆะซึ่งหมายความว่าพวกเขาไม่ได้อ้างถึงอะไรเลยในขณะนี้

ภายในประเภทการอ้างอิงถูกนำไปใช้เป็นพอยน์เตอร์และรู้ว่าและรู้ว่าการกำหนดตัวแปรทำงานอย่างไรมีรูปแบบพฤติกรรมอื่น ๆ :

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

เมื่อคุณประกาศตัวแปรหรือฟิลด์นี่คือความแตกต่างของสองประเภท:

  • ตัวแปรประเภทค่าชีวิตในกอง, ประเภทอ้างอิงชีวิตในกองเป็นตัวชี้ไปยังที่ใดที่หนึ่งในความทรงจำกองที่ชีวิตของหน่วยความจำที่เกิดขึ้นจริง (แม้ว่าโน้ตเอริค Lipperts บทความชุด: สแต็คเป็นรายละเอียดการดำเนินงาน .)
  • class / struct-field: ประเภทค่าอาศัยอยู่อย่างสมบูรณ์ภายในประเภทประเภทอ้างอิงอาศัยอยู่ในประเภทเป็นตัวชี้ไปยังที่ใดที่หนึ่งในหน่วยความจำฮีพที่หน่วยความจำจริงใช้งานอยู่

43
เพื่อประโยชน์ของความครบถ้วนสมบูรณ์ฉันควรพูดถึงว่า Eric Lippert ได้กล่าวไว้ว่าสแต็กนั้นเป็นรายละเอียดการนำไปปฏิบัติเมื่อใดก็ตามที่ฉันพูดถึงสแต็กด้านบนมีโพสต์ของ Eric อยู่ในใจ
Lasse V. Karlsen

2
ทั้งหมดนี้ใช้ได้กับ C ++ หรือไม่
Koray Tugay

9
ความแตกต่างที่สำคัญอีกอย่างก็คือการใช้งาน จาก MSDN: "structs มักจะใช้เพื่อห่อหุ้มกลุ่มเล็ก ๆ ของตัวแปรที่เกี่ยวข้องเช่นพิกัดของสี่เหลี่ยมผืนผ้า Structs ยังสามารถมีตัวสร้าง, ค่าคงที่, เขตข้อมูล, วิธีการ, คุณสมบัติ, ดัชนี, ผู้ประกอบการเหตุการณ์และประเภทที่ซ้อนกัน จำเป็นต้องมีสมาชิกคุณควรพิจารณาทำให้ประเภทของคุณเป็นคลาสแทน "
thewpfguy

4
@KorayTugay ไม่มันไม่ใช่
ZoomIn

9
@KorayTugay ใน C ++ struct และชั้นเป็นอย่างเทียบเท่ายกเว้นสิ่งหนึ่ง - ข้อ จำกัด การเข้าถึงเริ่มต้น (ชั้นมีภาคเอกชนโดยค่าเริ่มต้น struct มีประชาชน)
Berkus

207

สรุปสั้น ๆ ของแต่ละ:

ชั้นเรียนเท่านั้น:

  • สามารถรองรับมรดก
  • เป็นประเภทอ้างอิง (ตัวชี้)
  • การอ้างอิงอาจเป็นโมฆะ
  • มีค่าใช้จ่ายหน่วยความจำต่ออินสแตนซ์ใหม่

โครงสร้างเท่านั้น:

  • ไม่สามารถรองรับการสืบทอด
  • เป็นประเภทค่า
  • ถูกส่งผ่านตามค่า (เช่นจำนวนเต็ม)
  • ไม่สามารถมีการอ้างอิงที่เป็นโมฆะ (เว้นแต่จะใช้ Nullable)
  • ไม่มีค่าใช้จ่ายหน่วยความจำต่ออินสแตนซ์ใหม่ - ยกเว้น 'กล่อง'

ทั้งคลาสและโครงสร้าง:

  • ชนิดข้อมูลแบบผสมมักใช้เพื่อบรรจุตัวแปรบางตัวที่มีความสัมพันธ์เชิงตรรกะ
  • สามารถมีวิธีการและกิจกรรม
  • สามารถรองรับอินเตอร์เฟส

16
มีคำตอบบางส่วนที่ไม่ถูกต้อง คลาสไม่เสมอไปบนฮีปและ structs มักจะไม่ลงบนสแต็ก ข้อยกเว้นปัจจุบันรวมถึงเขตข้อมูล struct ในชั้นเรียนตัวแปรที่จับภาพในวิธีการที่ไม่ระบุชื่อและการแสดงออกแลมบ์ดาบล็อกตัววนซ้ำและค่ากล่องที่กล่าวถึงแล้ว แต่การจัดสรรสแต็คกับฮีปเป็นรายละเอียดการนำไปปฏิบัติและอาจมีการเปลี่ยนแปลง เอริค lippart กล่าวถึงที่นี่ ฉันลงคะแนนแล้ว แต่จะลบอย่างมีความสุขหากคุณอัปเดต
Simon P Stevens

1
struct ไม่สนับสนุนการสืบทอดจาก stucts / คลาสอื่น แต่คุณสามารถใช้อินเตอร์เฟสบน struct ได้
thewpfguy

2
คุณอาจต้องการที่จะอธิบายสิ่งที่คุณหมายถึงเมื่อคุณอ้างว่า structs "ไม่ได้มีค่าใช้จ่ายต่อหน่วยความจำตัวอย่างใหม่" การตีความครั้งแรกของฉันคือการที่คุณอ้างว่า - ไร้สาระ - โครงสร้างนั้นใช้หน่วยความจำศูนย์ จากนั้นฉันคิดว่าบางทีคุณกำลังพยายามพูดว่า struct ซึ่งไม่เหมือนคลาสต้องการหน่วยความจำมากเท่ากับผลรวมของเขตข้อมูลสมาชิกและไม่ต้องเพิ่มเติม แต่จากนั้นฉันก็หา Google c# struct memory overheadและพบคำตอบของฮันส์แพสแทนท์ที่บอกว่าไม่นั่นก็ไม่ใช่อย่างนั้น ดังนั้นสิ่งที่ทำคุณหมายความว่าอย่างไร
Mark Amery

4
@MarkAmery ผมมีปฏิกิริยาเริ่มต้นเช่นเดียวกับคุณ ID เพื่อสำนวนที่ว่า "ไม่มีค่าใช้จ่ายในความทรงจำ" แต่ผมคิดว่า OP จะหมายถึงความจริงที่ว่ากรณีของการclassที่มีการจัดการหน่วยความจำ (การจัดการโดยการเก็บขยะ) ในขณะที่กรณีของการstructไม่ได้ .
ฮัทช์

1
"โครงสร้างถูกส่งผ่านโดยค่า (เช่นจำนวนเต็ม)" เป็นเท็จ: ตัวแปรทั้งหมดจะถูกส่งผ่านตามค่าเช่นเดียวกับประเภทการอ้างอิง หากคุณต้องการส่งผ่านตัวแปรโดยการอ้างอิงคุณต้องใช้คำหลัก "อ้างอิง" jonskeet.uk/csharp/parameters.html#ref
Marco Staffoli

41

ใน. NET การประกาศคลาสและการประกาศแยกความแตกต่างระหว่างชนิดการอ้างอิงและชนิดของค่า

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

เมื่อคุณผ่านประเภทของค่าแต่ละชนิดเป็นสำเนา รหัสทั้งหมดทำงานบนสำเนาของตัวเอง

สิ่งนี้สามารถแสดงได้ด้วยตัวอย่าง:

struct MyStruct 
{
    string MyProperty { get; set; }
}

void ChangeMyStruct(MyStruct input) 
{ 
   input.MyProperty = "new value";
}

...

// Create value type
MyStruct testStruct = new MyStruct { MyProperty = "initial value" }; 

ChangeMyStruct(testStruct);

// Value of testStruct.MyProperty is still "initial value"
// - the method changed a new copy of the structure.

สำหรับชั้นเรียนนี้จะแตกต่างกัน

class MyClass 
{
    string MyProperty { get; set; }
}

void ChangeMyClass(MyClass input) 
{ 
   input.MyProperty = "new value";
}

...

// Create reference type
MyClass testClass = new MyClass { MyProperty = "initial value" };

ChangeMyClass(testClass);

// Value of testClass.MyProperty is now "new value" 
// - the method changed the instance passed.

คลาสสามารถเป็นอะไรก็ได้ - การอ้างอิงสามารถชี้ไปที่โมฆะ

โครงสร้างเป็นค่าจริง - พวกเขาสามารถว่างเปล่า แต่ไม่เคยเป็นโมฆะ ด้วยเหตุผลนี้ structs มักจะมีคอนสตรัคเตอร์เริ่มต้นโดยไม่มีพารามิเตอร์ - พวกเขาต้องการ


@ T.Todua ใช่มีคำตอบที่ดีกว่าที่ฉันโหวตและเลือกเป็นคำตอบหลังจากให้หนึ่ง - นี้มาจากเบต้าแรกของ SO เมื่อเรายังคงหากฎ
Keith

1
ฉันไม่ทราบว่าคุณเข้าใจฉันอย่างถูกต้องหรือไม่ฉันยอมรับ / ยอมรับคำตอบของคุณ (ตรงข้ามกับคำตอบข้างต้น) เพราะคุณมีตัวอย่างที่ดี (ไม่ใช่แค่คำอธิบายทางทฤษฎีเท่านั้น )
T.Todua

24

ความแตกต่างระหว่างโครงสร้างและคลาส:

  • structs เป็นประเภทค่าในขณะที่การเรียนการสอนเป็นประเภทการอ้างอิง
  • Structs จะถูกเก็บไว้ในสแตกในขณะที่เรียนจะถูกเก็บไว้ในกอง
  • Value types เก็บค่าไว้ในหน่วยความจำที่มีการประกาศ แต่ประเภทการอ้างอิงเก็บการอ้างอิงไปยังหน่วยความจำวัตถุ
  • ชนิดของค่าถูกทำลายทันทีหลังจากขอบเขตหายไปในขณะที่ประเภทการอ้างอิงเท่านั้นตัวแปรทำลายหลังจากขอบเขตหายไป วัตถุถูกทำลายโดยตัวเก็บรวบรวมขยะในภายหลัง
  • เมื่อคุณคัดลอก struct ไปยังอีกโครงสร้างหนึ่งสำเนาใหม่ของโครงสร้างนั้นจะถูกสร้างขึ้นแก้ไขจากโครงสร้างหนึ่งจะไม่ส่งผลกระทบต่อมูลค่าของโครงสร้างอื่น
  • เมื่อคุณคัดลอกคลาสไปยังคลาสอื่นมันจะคัดลอกตัวแปรอ้างอิงเท่านั้น
  • ทั้งตัวแปรอ้างอิงชี้ไปที่วัตถุเดียวกันบนฮีป การเปลี่ยนเป็นตัวแปรหนึ่งจะมีผลกับตัวแปรอ้างอิงอื่น
  • โครงสร้างไม่สามารถมี destructors ได้แต่คลาสสามารถมี destructors ได้
  • โครงสร้างไม่สามารถมีตัวสร้างแบบไม่มีพารามิเตอร์ที่ชัดเจนในขณะที่คลาสสามารถโครงสร้างไม่สนับสนุนการสืบทอด แต่คลาสมี ทั้งสนับสนุนการสืบทอดจากอินเตอร์เฟส
  • Structs ที่ปิดผนึกชนิด

21

จากการเลือกระหว่าง Microsoft กับคลาสและโครงสร้าง ...

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

พิจารณาโครงสร้างแทนคลาส:

  • หากอินสแตนซ์ของชนิดมีขนาดเล็กและอายุสั้นทั่วไปหรือถูกฝังอยู่ในวัตถุอื่น ๆ

X หลีกเลี่ยง structยกเว้นว่าชนิดมีคุณสมบัติดังต่อไปนี้ทั้งหมด:

  • มันมีเหตุผลแสดงถึงค่าเดียวคล้ายกับประเภทดั้งเดิม (int, double, ฯลฯ )
  • มีขนาดอินสแตนซ์ที่ต่ำกว่า 16 ไบต์
  • มันไม่เปลี่ยนรูป (ไม่สามารถเปลี่ยนแปลงได้)
  • มันจะไม่ต้องมีกล่องบ่อย

19

นอกเหนือจากความแตกต่างทั้งหมดที่อธิบายไว้ในคำตอบอื่น ๆ :

  1. โครงสร้างไม่สามารถมีตัวสร้างพารามิเตอร์ที่ไม่ชัดเจนในขณะที่คลาสสามารถ
  2. โครงสร้างไม่สามารถมี destructorsในขณะที่ชั้นเรียนสามารถ
  3. โครงสร้างไม่สามารถสืบทอดจากโครงสร้างหรือคลาสอื่นได้ในขณะที่คลาสสามารถสืบทอดจากคลาสอื่นได้ (ทั้งโครงสร้างและคลาสสามารถใช้งานได้จากส่วนต่อประสาน)

หากคุณหลังจากวิดีโออธิบายความแตกต่างทั้งหมดที่คุณสามารถตรวจสอบส่วนที่ 29 - C # Tutorial - ความแตกต่างระหว่างการเรียนและ structs ใน C #


4
มีความสำคัญมากกว่าความจริงที่ว่า. net language โดยทั่วไปจะไม่ยอมให้ struct กำหนด constructor แบบไม่มีพารามิเตอร์ (การตัดสินใจว่าจะอนุญาตหรือไม่ให้สร้างโดยคอมไพเลอร์ภาษา) คือความจริงที่ว่าโครงสร้างสามารถเกิดขึ้นได้และถูกเปิดเผย สู่โลกภายนอกที่ไม่มีตัวสร้างแบบใด ๆ ที่ถูกเรียกใช้ (แม้ว่าจะกำหนดตัวสร้างแบบไม่ใช้พารามิเตอร์) เหตุผลทั่วไปภาษา. net ห้ามการสร้างแบบไม่มีพารามิเตอร์สำหรับ structs คือการหลีกเลี่ยงความสับสนที่อาจเกิดขึ้นจากการมี constructors ดังกล่าวบางครั้งทำงานและบางครั้งไม่
supercat

15

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

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

ความแตกต่างจะดีขึ้นโดยชื่อ C ++ / CLI: "ref class" เป็นคลาสตามที่อธิบายไว้ก่อน "value class" เป็นคลาสตามที่อธิบายไว้ที่สอง คำหลัก "class" และ "struct" ที่ใช้โดย C # เป็นเพียงสิ่งที่ต้องเรียนรู้


11
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
|                        |                                                Struct                                                |                                               Class                                               |
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| Type                   | Value-type                                                                                           | Reference-type                                                                                    |
| Where                  | On stack / Inline in containing type                                                                 | On Heap                                                                                           |
| Deallocation           | Stack unwinds / containing type gets deallocated                                                     | Garbage Collected                                                                                 |
| Arrays                 | Inline, elements are the actual instances of the value type                                          | Out of line, elements are just references to instances of the reference type residing on the heap |
| Aldel Cost             | Cheap allocation-deallocation                                                                        | Expensive allocation-deallocation                                                                 |
| Memory usage           | Boxed when cast to a reference type or one of the interfaces they implement,                         | No boxing-unboxing                                                                                |
|                        | Unboxed when cast back to value type                                                                 |                                                                                                   |
|                        | (Negative impact because boxes are objects that are allocated on the heap and are garbage-collected) |                                                                                                   |
| Assignments            | Copy entire data                                                                                     | Copy the reference                                                                                |
| Change to an instance  | Does not affect any of its copies                                                                    | Affect all references pointing to the instance                                                    |
| Mutability             | Should be immutable                                                                                  | Mutable                                                                                           |
| Population             | In some situations                                                                                   | Majority of types in a framework should be classes                                                |
| Lifetime               | Short-lived                                                                                          | Long-lived                                                                                        |
| Destructor             | Cannot have                                                                                          | Can have                                                                                          |
| Inheritance            | Only from an interface                                                                               | Full support                                                                                      |
| Polymorphism           | No                                                                                                   | Yes                                                                                               |
| Sealed                 | Yes                                                                                                  | When have sealed keyword                                                                          |
| Constructor            | Can not have explicit parameterless constructors                                                     | Any constructor                                                                                   |
| Null-assignments       | When marked with nullable question mark                                                              | Yes (+ When marked with nullable question mark in C# 8+)                                          |
| Abstract               | No                                                                                                   | When have abstract keyword                                                                        |
| Member Access Modifiers| public, private, internal                                                                            | public, protected, internal, protected internal, private protected                                |
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+

1
อันที่จริงแล้วมันยอดเยี่ยมมาก: สรุปและให้ข้อมูล โปรดจำไว้ว่าให้อ่านคำตอบของคุณอย่างน้อยหนึ่งครั้ง - คุณสลับโครงสร้างและคำอธิบายคลาสในบางแถวและยังมีการพิมพ์ผิด
Robert Synoradzki

1
@ensisNoctis ขออภัยในความผิดพลาดเหล่านั้นและขอขอบคุณสำหรับการแก้ไข ฉันควรอ่านคำตอบของฉันอีกครั้ง😅
0xaryan

8

โครงสร้างกับคลาส

โครงสร้างเป็นชนิดค่าดังนั้นมันจึงถูกเก็บไว้ในสแต็ก แต่คลาสเป็นชนิดการอ้างอิงและถูกเก็บไว้ในกอง

โครงสร้างไม่รองรับการสืบทอดและ polymorphism แต่คลาสรองรับทั้งสองอย่าง

โดยค่าเริ่มต้นสมาชิก struct ทั้งหมดเป็นแบบสาธารณะ แต่สมาชิกคลาสจะเป็นแบบส่วนตัวโดยค่าเริ่มต้น

เนื่องจากโครงสร้างเป็นชนิดค่าเราจึงไม่สามารถกำหนดค่า Null ให้กับวัตถุ struct ได้ แต่ไม่ใช่กรณีของคลาส


5
เกี่ยวกับ "สมาชิก struct ทั้งหมดเป็นแบบสาธารณะ": ถ้าฉันไม่เข้าใจผิดนั่นไม่ถูกต้อง "ระดับการเข้าถึงสำหรับสมาชิกของคลาสและสมาชิกโครงสร้างรวมถึงคลาสที่ซ้อนกันและโครงสร้างเป็นส่วนตัวโดยค่าเริ่มต้น" msdn.microsoft.com/en-us/library/ms173121.aspx
Nate Cook

8

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

  • ด้วย struct อาร์เรย์มีตัวอย่างของ struct
  • ด้วยคลาสอาร์เรย์ประกอบด้วยตัวชี้ไปยังอินสแตนซ์ของคลาสอื่น ๆ ในหน่วยความจำ

ดังนั้นอาร์เรย์ของ structs จึงมีลักษณะเช่นนี้ในหน่วยความจำ

[struct][struct][struct][struct][struct][struct][struct][struct]

ในขณะที่อาเรย์ของคลาสมีลักษณะเช่นนี้

[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]

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

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

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

นี่คือรหัสที่คุณสามารถทดสอบได้ บนเครื่องของฉันการวนซ้ำในคลาสอาเรย์ใช้เวลานานกว่าอาเรย์ struct ประมาณ 3 เท่า

    private struct PerformanceStruct
    {
        public int i1;
        public int i2;
    }

    private class PerformanceClass
    {
        public int i1;
        public int i2;
    }

    private static void DoTest()
    {
        var structArray = new PerformanceStruct[100000000];
        var classArray = new PerformanceClass[structArray.Length];

        for (var i = 0; i < structArray.Length; i++)
        {
            structArray[i] = new PerformanceStruct();
            classArray[i] = new PerformanceClass();
        }

        long total = 0;
        var sw = new Stopwatch();
        sw.Start();
        for (var loops = 0; loops < 100; loops++)
        for (var i = 0; i < structArray.Length; i++)
        {
            total += structArray[i].i1 + structArray[i].i2;
        }

        sw.Stop();
        Console.WriteLine($"Struct Time: {sw.ElapsedMilliseconds}");
        sw = new Stopwatch();
        sw.Start();
        for (var loops = 0; loops < 100; loops++)
        for (var i = 0; i < classArray.Length; i++)
        {
            total += classArray[i].i1 + classArray[i].i2;
        }

        Console.WriteLine($"Class Time: {sw.ElapsedMilliseconds}");
    }

-1; "Structs เป็นประเภทค่าดังนั้นพวกเขาจึงเก็บค่าคลาสเป็นประเภทอ้างอิงดังนั้นจึงอ้างอิงคลาส" ไม่ชัดเจนและไม่น่าเป็นไปได้สำหรับใครที่ยังไม่เข้าใจจากคำตอบอื่น ๆ ที่นี่และ"ด้วยคลาสคลาสที่ประกอบด้วยคลาสจะประกอบด้วยตัวชี้ไปยังคลาสใหม่ในพื้นที่หน่วยความจำที่แตกต่างกัน" สับสนคลาสกับอินสแตนซ์ของคลาส
Mark Amery

@ MarkAmery ฉันพยายามชี้แจงเล็กน้อย จุดที่ฉันพยายามทำจริงๆคือความแตกต่างในวิธีที่อาร์เรย์ทำงานกับค่าและประเภทการอ้างอิงและผลกระทบที่มีต่อประสิทธิภาพ ฉันไม่ได้พยายามอธิบายสิ่งที่มีค่าและประเภทอ้างอิงอีกเช่นนี้เพราะคำตอบอื่น ๆ มากมาย
Will Calderwood

7

เพียงเพื่อให้เสร็จสมบูรณ์มีความแตกต่างเมื่อใช้Equalsวิธีการซึ่งสืบทอดมาจากคลาสและโครงสร้างทั้งหมด

มาพูดกันว่าเรามีคลาสและโครงสร้าง:

class A{
  public int a, b;
}
struct B{
  public int a, b;
}

และในวิธีการหลักเรามี 4 วัตถุ

static void Main{
  A c1 = new A(), c2 = new A();
  c1.a = c1.b = c2.a = c2.b = 1;
  B s1 = new B(), s2 = new B();
  s1.a = s1.b = s2.a = s2.b = 1;
}

แล้ว:

s1.Equals(s2) // true
s1.Equals(c1) // false
c1.Equals(c2) // false
c1 == c2 // false

ดังนั้นโครงสร้างจึงเหมาะสำหรับวัตถุที่เป็นตัวเลขเช่นจุด (บันทึก x และพิกัด y) และคลาสเหมาะสำหรับผู้อื่น แม้ว่า 2 คนจะมีชื่อความสูงน้ำหนัก ... พวกเขายังคงเป็น 2 คน


6

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

คนอื่น ๆ อาจให้รายละเอียดมากกว่าคุณ แต่ฉันใช้ structs เมื่อโครงสร้างที่ฉันต้องการนั้นเรียบง่าย


4

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

structs:

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

ประเภท:

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

ตัวอย่างโค้ด

    static void Main(string[] args)
    {
        //Struct
        myStruct objStruct = new myStruct();
        objStruct.x = 10;
        Console.WriteLine("Initial value of Struct Object is: " + objStruct.x);
        Console.WriteLine();
        methodStruct(objStruct);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Struct Object is: " + objStruct.x);
        Console.WriteLine();

        //Class
        myClass objClass = new myClass(10);
        Console.WriteLine("Initial value of Class Object is: " + objClass.x);
        Console.WriteLine();
        methodClass(objClass);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Class Object is: " + objClass.x);
        Console.Read();
    }
    static void methodStruct(myStruct newStruct)
    {
        newStruct.x = 20;
        Console.WriteLine("Inside Struct Method");
        Console.WriteLine("Inside Method value of Struct Object is: " + newStruct.x);
    }
    static void methodClass(myClass newClass)
    {
        newClass.x = 20;
        Console.WriteLine("Inside Class Method");
        Console.WriteLine("Inside Method value of Class Object is: " + newClass.x);
    }
    public struct myStruct
    {
        public int x;
        public myStruct(int xCons)
        {
            this.x = xCons;
        }
    }
    public class myClass
    {
        public int x;
        public myClass(int xCons)
        {
            this.x = xCons;
        }
    }

เอาท์พุต

ค่าเริ่มต้นของโครงสร้างวัตถุคือ: 10

Inside Method Method ค่า Inside Method ของ Struct Object คือ: 20

หลังจากค่าการเรียกใช้เมธอดของ Struct Object คือ: 10

ค่าเริ่มต้นของ Class Object คือ: 10

Inside Class Method ค่า Inside Method ของ Class Object คือ: 20

หลังจากค่าการเรียกใช้เมธอดของ Class Object คือ: 20

ที่นี่คุณสามารถเห็นความแตกต่างระหว่างการโทรตามค่าและการโทรโดยอ้างอิงได้อย่างชัดเจน


4
  1. เหตุการณ์ที่ประกาศในคลาสจะมีการเข้าถึง + = และ - = การล็อกโดยอัตโนมัติผ่านการล็อก (สิ่งนี้) เพื่อทำให้เธรดปลอดภัย (เหตุการณ์สแตติกจะถูกล็อคในประเภทของคลาส) เหตุการณ์ที่ประกาศใน struct ไม่มีการเข้าถึง + = และ - = โดยอัตโนมัติ การล็อค (นี่) สำหรับโครงสร้างจะไม่ทำงานเนื่องจากคุณสามารถล็อคได้เฉพาะนิพจน์ประเภทการอ้างอิง

  2. การสร้างอินสแตนซ์ struct ไม่สามารถทำให้เกิดการรวบรวมขยะ (เว้นแต่ว่าตัวสร้างโดยตรงหรือโดยอ้อมสร้างตัวอย่างประเภทการอ้างอิง) ในขณะที่การสร้างอินสแตนซ์ประเภทการอ้างอิงอาจทำให้เกิดการเก็บขยะ

  3. struct มักจะมีตัวสร้างค่าเริ่มต้นสาธารณะในตัวเสมอ

    class DefaultConstructor
    {
        static void Eg()
        {
            Direct     yes = new   Direct(); // Always compiles OK
            InDirect maybe = new InDirect(); // Compiles if constructor exists and is accessible
            //...
        }
    }

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

    class NonInstantiable
    {
        private NonInstantiable() // OK
        {
        }
    }
    
    struct Direct
    {
        private Direct() // Compile-time error
        {
        }
    }
  4. struct ไม่สามารถมี destructor ได้ destructor เป็นเพียงการแทนที่ของวัตถุสิ้นสุดในการปลอมตัวและ structs เป็นชนิดของค่าไม่อยู่ภายใต้การรวบรวมขยะ

    struct Direct
    {
        ~Direct() {} // Compile-time error
    }
    class InDirect
    {
        ~InDirect() {} // Compiles OK
    }
    
    And the CIL for ~Indirect() looks like this:
    
    .method family hidebysig virtual instance void
            Finalize() cil managed
    {
      // ...
    } // end of method Indirect::Finalize
  5. struct ถูกผนึกโดยปริยายคลาสไม่ได้
    struct ไม่สามารถเป็นนามธรรมคลาสสามารถ
    struct ไม่สามารถเรียก: base () ในนวกรรมิกในขณะที่คลาสที่ไม่มีคลาสพื้นฐานที่ชัดเจนสามารถ
    struct ไม่สามารถขยายคลาสอีกคลาสได้
    struct ไม่สามารถประกาศสมาชิกที่ได้รับการป้องกัน (ตัวอย่างเช่นเขตข้อมูลชนิดซ้อนกัน) ที่คลาสสามารถทำได้
    struct ไม่สามารถประกาศสมาชิกฟังก์ชันนามธรรมคลาสนามธรรมสามารถ
    struct ไม่สามารถประกาศสมาชิกฟังก์ชันเสมือนคลาสได้
    struct ไม่สามารถประกาศสมาชิกฟังก์ชันที่ปิดผนึกคลาสสามารถ
    struct ไม่สามารถประกาศการแทนที่ฟังก์ชันสมาชิกคลาสสามารถ
    ข้อยกเว้นเดียวสำหรับกฎนี้คือโครงสร้างสามารถแทนที่วิธีเสมือนของ System.Object, viz, Equals () และ GetHashCode () และ ToString ()


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

@supercat ใช่ใช่เหตุการณ์ที่ไม่คงที่ใน struct จะแปลกมากและมันจะมีประโยชน์เฉพาะสำหรับ structable ที่ไม่แน่นอนและเหตุการณ์นั้นเอง (ถ้าเป็นเหตุการณ์ "คล้ายสนาม") เปลี่ยน struct ให้เป็น "ไม่แน่นอน หมวดหมู่ "และยังแนะนำฟิลด์ประเภทอ้างอิงในโครงสร้าง เหตุการณ์ที่ไม่คงที่ใน structs ต้องชั่วร้าย
Jeppe Stig Nielsen

@JeppeStigNielsen: รูปแบบเดียวที่ฉันสามารถเห็นได้ว่ามันจะเหมาะสมสำหรับ struct ที่จะมีเหตุการณ์คือถ้าวัตถุประสงค์ของ struct คือการเก็บการอ้างอิงที่ไม่เปลี่ยนรูปไปยังคลาสวัตถุที่มันทำหน้าที่เป็นตัวแทน เหตุการณ์อัตโนมัติจะไร้ประโยชน์โดยสิ้นเชิงในสถานการณ์ดังกล่าว การสมัครและยกเลิกการสมัครรับข่าวสารจะต้องส่งต่อไปยังคลาสที่อยู่ด้านหลังโครงสร้างแทน ฉันหวังว่า. NET มี (หรือจะทำให้เป็นไปได้ที่จะกำหนด) ประเภทโครงสร้าง 'แคชกล่อง "ที่มีเขตข้อมูลเริ่มต้นที่ซ่อนโมฆะประเภทObjectซึ่งจะถือการอ้างอิงไปยังสำเนาของโครงสร้างของกล่อง
supercat

1
@JeppeStigNielsen: Structs มีประสิทธิภาพสูงกว่าคลาสในหลาย ๆ สถานการณ์การใช้พร็อกซี ปัญหาที่ใหญ่ที่สุดของการใช้ structs คือในกรณีที่การชกมวยเป็นสิ่งจำเป็นมันมักจะจบลงด้วยการเลื่อนไปเป็นวงใน หากมีวิธีการหลีกเลี่ยงการถูกวางโครงสร้างซ้ำ ๆพวกเขาจะดีกว่าคลาสในสถานการณ์การใช้งานอื่น ๆ อีกมากมาย
supercat

4

ดังที่ได้กล่าวไว้ก่อนหน้านี้: คลาสเป็นประเภทอ้างอิงในขณะที่โครงสร้างเป็นประเภทค่าที่มีผลกระทบทั้งหมด

ในฐานะที่เป็นกฎทั่วไปของแนวทางการออกแบบ Framework แนะนำให้ใช้ Structs แทนคลาสหาก:

  • มีขนาดอินสแตนซ์ที่ต่ำกว่า 16 ไบต์
  • มันมีเหตุผลแสดงถึงค่าเดียวคล้ายกับประเภทดั้งเดิม (int, double, ฯลฯ )
  • มันไม่เปลี่ยนรูป
  • มันจะไม่ต้องมีกล่องบ่อย

3

มีหนึ่งกรณีที่น่าสนใจของตัวต่อปริศนา "class vs struct" - สถานการณ์เมื่อคุณต้องการส่งคืนผลลัพธ์จำนวนมากจากวิธีการ: เลือกสิ่งที่จะใช้ ถ้าคุณรู้เรื่อง ValueTuple - คุณรู้ว่า ValueTuple (struct) ถูกเพิ่มเพราะมันควรจะมีประสิทธิภาพมากกว่า Tuple (คลาส) แต่มันหมายความว่าอะไรในตัวเลข? การทดสอบสองครั้ง: หนึ่งคือ struct / class ที่มี 2 ฟิลด์อื่น ๆ ที่มี struct / class ที่มี 8 ฟิลด์ (ที่มีขนาดมากกว่า 4 คลาสควรมีประสิทธิภาพมากกว่าจากนั้น struct ในเงื่อนไขตัวประมวลผลเห็บ แต่แน่นอนว่าควรพิจารณา GC load ด้วย )

PS มาตรฐานอื่นสำหรับ 'sturct หรือคลาสที่มีคอลเล็กชัน' กรณีเฉพาะมี: https://stackoverflow.com/a/45276657/506147

BenchmarkDotNet=v0.10.10, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233540 Hz, Resolution=309.2586 ns, Timer=TSC
.NET Core SDK=2.0.3
  [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
  Clr    : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2115.0
  Core   : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT


            Method |  Job | Runtime |     Mean |     Error |    StdDev |      Min |      Max |   Median | Rank |  Gen 0 | Allocated |
------------------ |----- |-------- |---------:|----------:|----------:|---------:|---------:|---------:|-----:|-------:|----------:|
  TestStructReturn |  Clr |     Clr | 17.57 ns | 0.1960 ns | 0.1834 ns | 17.25 ns | 17.89 ns | 17.55 ns |    4 | 0.0127 |      40 B |
   TestClassReturn |  Clr |     Clr | 21.93 ns | 0.4554 ns | 0.5244 ns | 21.17 ns | 23.26 ns | 21.86 ns |    5 | 0.0229 |      72 B |
 TestStructReturn8 |  Clr |     Clr | 38.99 ns | 0.8302 ns | 1.4097 ns | 37.36 ns | 42.35 ns | 38.50 ns |    8 | 0.0127 |      40 B |
  TestClassReturn8 |  Clr |     Clr | 23.69 ns | 0.5373 ns | 0.6987 ns | 22.70 ns | 25.24 ns | 23.37 ns |    6 | 0.0305 |      96 B |
  TestStructReturn | Core |    Core | 12.28 ns | 0.1882 ns | 0.1760 ns | 11.92 ns | 12.57 ns | 12.30 ns |    1 | 0.0127 |      40 B |
   TestClassReturn | Core |    Core | 15.33 ns | 0.4343 ns | 0.4063 ns | 14.83 ns | 16.44 ns | 15.31 ns |    2 | 0.0229 |      72 B |
 TestStructReturn8 | Core |    Core | 34.11 ns | 0.7089 ns | 1.4954 ns | 31.52 ns | 36.81 ns | 34.03 ns |    7 | 0.0127 |      40 B |
  TestClassReturn8 | Core |    Core | 17.04 ns | 0.2299 ns | 0.2150 ns | 16.68 ns | 17.41 ns | 16.98 ns |    3 | 0.0305 |      96 B |

การทดสอบรหัส:

using System;
using System.Text;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using DashboardCode.Routines.Json;

namespace Benchmark
{
    //[Config(typeof(MyManualConfig))]
    [RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
    [ClrJob, CoreJob]
    [HtmlExporter, MarkdownExporter]
    [MemoryDiagnoser]
    public class BenchmarkStructOrClass
    {
        static TestStruct testStruct = new TestStruct();
        static TestClass testClass = new TestClass();
        static TestStruct8 testStruct8 = new TestStruct8();
        static TestClass8 testClass8 = new TestClass8();
        [Benchmark]
        public void TestStructReturn()
        {
            testStruct.TestMethod();
        }

        [Benchmark]
        public void TestClassReturn()
        {
            testClass.TestMethod();
        }


        [Benchmark]
        public void TestStructReturn8()
        {
            testStruct8.TestMethod();
        }

        [Benchmark]
        public void TestClassReturn8()
        {
            testClass8.TestMethod();
        }

        public class TestStruct
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }

            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }

        public class TestClass
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }

            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }

        public class TestStruct8
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }

            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }

        public class TestClass8
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }

            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }
    }
}

2

โครงสร้างเป็นค่าจริง - พวกเขาสามารถว่างเปล่า แต่ไม่เคยเป็นโมฆะ

สิ่งนี้เป็นความจริงอย่างไรก็ตามยังทราบว่าในฐานะของ. NET 2 structs สนับสนุนเวอร์ชัน Nullable และ C # ให้น้ำตาล syntactic บางอย่างเพื่อให้ง่ายต่อการใช้

int? value = null;
value  = 1;

1
โปรดทราบว่านี่เป็นเพียงวากยสัมพันธ์น้ำตาลที่อ่าน 'Nullable <int> value = null;'
Erik van Brakel

@ErikvanBrakel นั่นไม่ใช่แค่น้ำตาลทราย กฎการชกมวยที่แตกต่างกันหมายถึง(object)(default(int?)) == nullคุณไม่สามารถทำอะไรกับค่าประเภทอื่น ๆ ได้เพราะมันมีมากกว่าแค่น้ำตาลที่เกิดขึ้นที่นี่ น้ำตาลเพียงอย่างเดียวคือสำหรับint? Nullable<int>
Jon Hanna

-1; สิ่งนี้ไม่ได้ตอบคำถามว่าอะไรคือความแตกต่างระหว่าง structs และคลาสและเช่นนี้คุณควรได้รับความคิดเห็นเกี่ยวกับคำตอบที่คุณตอบกลับไม่ใช่คำตอบแยกต่างหาก (แม้ว่าบางทีบรรทัดฐานของไซต์อาจแตกต่างกันในเดือนสิงหาคม 2008!)
Mark Amery

1

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

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

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

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

ใน. NET มันเป็นไปได้สำหรับชนิดของค่าที่จะใช้ความหมายใด ๆ ข้างต้นโดยที่ฟิลด์ทั้งหมดสามารถทำได้เช่นเดียวกัน อย่างไรก็ตามประเภทการอ้างอิงสามารถใช้ความหมายของการอ้างอิงที่ไม่แน่นอนหรือซีแมนทิกที่ไม่เปลี่ยนรูปได้เท่านั้น ชนิดของค่าที่มีเขตข้อมูลของประเภทการอ้างอิงที่ไม่แน่นอนจะ จำกัด อยู่ที่การใช้การอ้างอิงความหมายที่ไม่แน่นอนหรือความหมายไฮบริดแปลก

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