อะไรคือความแตกต่างระหว่าง struct และ class ใน. NET?
อะไรคือความแตกต่างระหว่าง struct และ class ใน. NET?
คำตอบ:
ใน .NET มีสองประเภทประเภทประเภทของการอ้างอิงและประเภทค่า
structs เป็นประเภทค่าและการเรียนเป็นประเภทของการอ้างอิง
ความแตกต่างทั่วไปคือประเภทการอ้างอิงมีชีวิตอยู่บนฮีปและประเภทค่าของการใช้ชีวิตแบบอินไลน์นั่นคือที่ใดก็ตามที่มันเป็นตัวแปรหรือเขตของคุณจะถูกกำหนด
ตัวแปรที่มีประเภทค่าประกอบด้วยค่าชนิดทั้งหมด สำหรับ struct หมายความว่าตัวแปรมีโครงสร้างทั้งหมดพร้อมฟิลด์ทั้งหมด
ตัวแปรที่มีประเภทการอ้างอิงประกอบด้วยตัวชี้หรือการอ้างอิงไปยังที่อื่นในหน่วยความจำที่มีค่าจริงอยู่
สิ่งนี้มีประโยชน์อย่างหนึ่งเริ่มต้นด้วย:
ภายในประเภทการอ้างอิงถูกนำไปใช้เป็นพอยน์เตอร์และรู้ว่าและรู้ว่าการกำหนดตัวแปรทำงานอย่างไรมีรูปแบบพฤติกรรมอื่น ๆ :
เมื่อคุณประกาศตัวแปรหรือฟิลด์นี่คือความแตกต่างของสองประเภท:
สรุปสั้น ๆ ของแต่ละ:
ชั้นเรียนเท่านั้น:
โครงสร้างเท่านั้น:
ทั้งคลาสและโครงสร้าง:
c# struct memory overhead
และพบคำตอบของฮันส์แพสแทนท์ที่บอกว่าไม่นั่นก็ไม่ใช่อย่างนั้น ดังนั้นสิ่งที่ทำคุณหมายความว่าอย่างไร
class
ที่มีการจัดการหน่วยความจำ (การจัดการโดยการเก็บขยะ) ในขณะที่กรณีของการstruct
ไม่ได้ .
ใน. 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 มักจะมีคอนสตรัคเตอร์เริ่มต้นโดยไม่มีพารามิเตอร์ - พวกเขาต้องการ
ความแตกต่างระหว่างโครงสร้างและคลาส:
จากการเลือกระหว่าง Microsoft กับคลาสและโครงสร้าง ...
ตามกฎของหัวแม่มือประเภทส่วนใหญ่ในกรอบควรเป็นชั้นเรียน อย่างไรก็ตามมีบางสถานการณ์ที่ลักษณะของชนิดค่าทำให้มันเหมาะสมกว่าที่จะใช้ structs
✓ พิจารณาโครงสร้างแทนคลาส:
- หากอินสแตนซ์ของชนิดมีขนาดเล็กและอายุสั้นทั่วไปหรือถูกฝังอยู่ในวัตถุอื่น ๆ
X หลีกเลี่ยง structยกเว้นว่าชนิดมีคุณสมบัติดังต่อไปนี้ทั้งหมด:
- มันมีเหตุผลแสดงถึงค่าเดียวคล้ายกับประเภทดั้งเดิม (int, double, ฯลฯ )
- มีขนาดอินสแตนซ์ที่ต่ำกว่า 16 ไบต์
- มันไม่เปลี่ยนรูป (ไม่สามารถเปลี่ยนแปลงได้)
- มันจะไม่ต้องมีกล่องบ่อย
นอกเหนือจากความแตกต่างทั้งหมดที่อธิบายไว้ในคำตอบอื่น ๆ :
หากคุณหลังจากวิดีโออธิบายความแตกต่างทั้งหมดที่คุณสามารถตรวจสอบส่วนที่ 29 - C # Tutorial - ความแตกต่างระหว่างการเรียนและ structs ใน C #
อินสแตนซ์ของคลาสจะถูกเก็บไว้ในฮีปที่มีการจัดการ อินสแตนซ์ 'ประกอบด้วย' ตัวแปรทั้งหมดเป็นเพียงการอ้างอิงถึงอินสแตนซ์บนฮีป การส่งวัตถุไปยังเมธอดจะส่งผลให้สำเนาของการอ้างอิงที่ส่งผ่านไม่ใช่ตัววัตถุเอง
โครงสร้าง (ในทางเทคนิคประเภทค่า) จะถูกเก็บไว้ทุกที่ที่พวกเขาใช้เช่นเดียวกับประเภทดั้งเดิม เนื้อหาอาจถูกคัดลอกโดยรันไทม์ได้ตลอดเวลาและไม่ต้องเรียกใช้ตัวสร้างสำเนาที่กำหนดเอง การส่งประเภทค่าไปยังวิธีการนั้นเกี่ยวข้องกับการคัดลอกค่าทั้งหมดอีกครั้งโดยไม่ต้องเรียกใช้รหัสที่กำหนดเองได้
ความแตกต่างจะดีขึ้นโดยชื่อ C ++ / CLI: "ref class" เป็นคลาสตามที่อธิบายไว้ก่อน "value class" เป็นคลาสตามที่อธิบายไว้ที่สอง คำหลัก "class" และ "struct" ที่ใช้โดย C # เป็นเพียงสิ่งที่ต้องเรียนรู้
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| | 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 |
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
โครงสร้างกับคลาส
โครงสร้างเป็นชนิดค่าดังนั้นมันจึงถูกเก็บไว้ในสแต็ก แต่คลาสเป็นชนิดการอ้างอิงและถูกเก็บไว้ในกอง
โครงสร้างไม่รองรับการสืบทอดและ polymorphism แต่คลาสรองรับทั้งสองอย่าง
โดยค่าเริ่มต้นสมาชิก struct ทั้งหมดเป็นแบบสาธารณะ แต่สมาชิกคลาสจะเป็นแบบส่วนตัวโดยค่าเริ่มต้น
เนื่องจากโครงสร้างเป็นชนิดค่าเราจึงไม่สามารถกำหนดค่า Null ให้กับวัตถุ 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}");
}
เพียงเพื่อให้เสร็จสมบูรณ์มีความแตกต่างเมื่อใช้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 คน
สำหรับผู้เริ่มแล้ว struct ถูกส่งผ่านตามค่าแทนที่จะอ้างอิง โครงสร้างเป็นสิ่งที่ดีสำหรับโครงสร้างข้อมูลที่ค่อนข้างง่ายในขณะที่คลาสมีความยืดหยุ่นมากขึ้นจากมุมมองทางสถาปัตยกรรมผ่านความหลากหลายและการสืบทอด
คนอื่น ๆ อาจให้รายละเอียดมากกว่าคุณ แต่ฉันใช้ structs เมื่อโครงสร้างที่ฉันต้องการนั้นเรียบง่าย
นอกจากความแตกต่างพื้นฐานของตัวระบุการเข้าถึงและมีเพียงไม่กี่คนที่กล่าวถึงข้างต้นฉันต้องการเพิ่มความแตกต่างที่สำคัญบางอย่างรวมถึงตัวอย่างที่กล่าวถึงข้างต้นด้วยโค้ดตัวอย่างที่มีเอาต์พุตซึ่งจะให้แนวคิดการอ้างอิงและค่าที่ชัดเจนยิ่งขึ้น
structs:
ประเภท:
ตัวอย่างโค้ด
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
ที่นี่คุณสามารถเห็นความแตกต่างระหว่างการโทรตามค่าและการโทรโดยอ้างอิงได้อย่างชัดเจน
เหตุการณ์ที่ประกาศในคลาสจะมีการเข้าถึง + = และ - = การล็อกโดยอัตโนมัติผ่านการล็อก (สิ่งนี้) เพื่อทำให้เธรดปลอดภัย (เหตุการณ์สแตติกจะถูกล็อคในประเภทของคลาส) เหตุการณ์ที่ประกาศใน struct ไม่มีการเข้าถึง + = และ - = โดยอัตโนมัติ การล็อค (นี่) สำหรับโครงสร้างจะไม่ทำงานเนื่องจากคุณสามารถล็อคได้เฉพาะนิพจน์ประเภทการอ้างอิง
การสร้างอินสแตนซ์ struct ไม่สามารถทำให้เกิดการรวบรวมขยะ (เว้นแต่ว่าตัวสร้างโดยตรงหรือโดยอ้อมสร้างตัวอย่างประเภทการอ้างอิง) ในขณะที่การสร้างอินสแตนซ์ประเภทการอ้างอิงอาจทำให้เกิดการเก็บขยะ
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
{
}
}
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
struct ถูกผนึกโดยปริยายคลาสไม่ได้
struct ไม่สามารถเป็นนามธรรมคลาสสามารถ
struct ไม่สามารถเรียก: base () ในนวกรรมิกในขณะที่คลาสที่ไม่มีคลาสพื้นฐานที่ชัดเจนสามารถ
struct ไม่สามารถขยายคลาสอีกคลาสได้
struct ไม่สามารถประกาศสมาชิกที่ได้รับการป้องกัน (ตัวอย่างเช่นเขตข้อมูลชนิดซ้อนกัน) ที่คลาสสามารถทำได้
struct ไม่สามารถประกาศสมาชิกฟังก์ชันนามธรรมคลาสนามธรรมสามารถ
struct ไม่สามารถประกาศสมาชิกฟังก์ชันเสมือนคลาสได้
struct ไม่สามารถประกาศสมาชิกฟังก์ชันที่ปิดผนึกคลาสสามารถ
struct ไม่สามารถประกาศการแทนที่ฟังก์ชันสมาชิกคลาสสามารถ
ข้อยกเว้นเดียวสำหรับกฎนี้คือโครงสร้างสามารถแทนที่วิธีเสมือนของ System.Object, viz, Equals () และ GetHashCode () และ ToString ()
Object
ซึ่งจะถือการอ้างอิงไปยังสำเนาของโครงสร้างของกล่อง
ดังที่ได้กล่าวไว้ก่อนหน้านี้: คลาสเป็นประเภทอ้างอิงในขณะที่โครงสร้างเป็นประเภทค่าที่มีผลกระทบทั้งหมด
ในฐานะที่เป็นกฎทั่วไปของแนวทางการออกแบบ Framework แนะนำให้ใช้ Structs แทนคลาสหาก:
มีหนึ่งกรณีที่น่าสนใจของตัวต่อปริศนา "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;
}
}
}
}
โครงสร้างเป็นค่าจริง - พวกเขาสามารถว่างเปล่า แต่ไม่เคยเป็นโมฆะ
สิ่งนี้เป็นความจริงอย่างไรก็ตามยังทราบว่าในฐานะของ. NET 2 structs สนับสนุนเวอร์ชัน Nullable และ C # ให้น้ำตาล syntactic บางอย่างเพื่อให้ง่ายต่อการใช้
int? value = null;
value = 1;
(object)(default(int?)) == null
คุณไม่สามารถทำอะไรกับค่าประเภทอื่น ๆ ได้เพราะมันมีมากกว่าแค่น้ำตาลที่เกิดขึ้นที่นี่ น้ำตาลเพียงอย่างเดียวคือสำหรับint?
Nullable<int>
ตัวแปรหรือเขตข้อมูลทุกประเภทของค่าดั้งเดิมหรือประเภทโครงสร้างมีอินสแตนซ์เฉพาะของประเภทนั้นรวมถึงฟิลด์ทั้งหมด (สาธารณะและส่วนตัว) ในทางกลับกันตัวแปรหรือฟิลด์ของประเภทการอ้างอิงอาจมีค่าเป็นโมฆะหรืออาจอ้างถึงวัตถุที่เก็บไว้ที่อื่นซึ่งอาจมีการอ้างอิงอื่นจำนวนเท่าใดก็ได้ เขตข้อมูลของโครงสร้างจะถูกเก็บไว้ในที่เดียวกับตัวแปรหรือเขตข้อมูลของชนิดโครงสร้างนั้นซึ่งอาจเป็นได้ทั้งในกองหรืออาจเป็นส่วนหนึ่งของวัตถุกองอื่น
การสร้างตัวแปรหรือฟิลด์ของชนิดค่าดั้งเดิมจะสร้างขึ้นด้วยค่าเริ่มต้น การสร้างตัวแปรหรือฟิลด์ของประเภทโครงสร้างจะสร้างอินสแตนซ์ใหม่โดยสร้างฟิลด์ทั้งหมดในลักษณะเริ่มต้น การสร้างอินสแตนซ์ใหม่ของประเภทการอ้างอิงจะเริ่มต้นด้วยการสร้างเขตข้อมูลทั้งหมดในนั้นในลักษณะเริ่มต้นแล้วเรียกใช้รหัสเพิ่มเติมเพิ่มเติมซึ่งขึ้นอยู่กับประเภท
การคัดลอกหนึ่งตัวแปรหรือเขตข้อมูลของชนิดดั้งเดิมไปยังอีกจะคัดลอกค่า การคัดลอกหนึ่งตัวแปรหรือฟิลด์ของโครงสร้างประเภทไปยังอีกจะคัดลอกฟิลด์ทั้งหมด (สาธารณะและส่วนตัว) ของอินสแตนซ์ก่อนหน้านี้ไปยังอินสแตนซ์หลัง การคัดลอกหนึ่งตัวแปรหรือฟิลด์ของประเภทการอ้างอิงไปยังอีกจะทำให้หลังเพื่ออ้างถึงอินสแตนซ์เดียวกันกับอดีต (ถ้ามี)
สิ่งสำคัญคือให้สังเกตว่าในบางภาษาเช่น C ++ พฤติกรรมเชิงความหมายของประเภทนั้นขึ้นอยู่กับวิธีการจัดเก็บ แต่ไม่เป็นจริงสำหรับ. NET หากชนิดใช้ค่าซีแมนทิกส์ที่ผันแปรได้การคัดลอกหนึ่งตัวแปรของประเภทนั้นไปยังอีกอันหนึ่งคัดลอกคุณสมบัติของอินสแตนซ์แรกไปยังอีกอินสแตนซ์หนึ่งซึ่งอ้างอิงโดยวินาทีและการใช้สมาชิกของวินาทีเพื่อกลาย แต่ไม่ใช่คนแรก หากประเภทดำเนินการอ้างอิงความหมายไม่แน่นอนการคัดลอกตัวแปรหนึ่งไปยังอีกและใช้สมาชิกของที่สองเพื่อกลายพันธุ์วัตถุจะส่งผลกระทบต่อวัตถุที่อ้างอิงโดยตัวแปรแรก; ประเภทที่มีซีแมนทิกส์ไม่เปลี่ยนรูปไม่อนุญาตการกลายพันธุ์ดังนั้นจึงไม่มีความหมายว่าการคัดลอกจะสร้างอินสแตนซ์ใหม่หรือสร้างการอ้างอิงอื่นไปที่แรก
ใน. NET มันเป็นไปได้สำหรับชนิดของค่าที่จะใช้ความหมายใด ๆ ข้างต้นโดยที่ฟิลด์ทั้งหมดสามารถทำได้เช่นเดียวกัน อย่างไรก็ตามประเภทการอ้างอิงสามารถใช้ความหมายของการอ้างอิงที่ไม่แน่นอนหรือซีแมนทิกที่ไม่เปลี่ยนรูปได้เท่านั้น ชนิดของค่าที่มีเขตข้อมูลของประเภทการอ้างอิงที่ไม่แน่นอนจะ จำกัด อยู่ที่การใช้การอ้างอิงความหมายที่ไม่แน่นอนหรือความหมายไฮบริดแปลก