Jawaban:
Di .NET, ada dua kategori tipe, tipe referensi dan tipe nilai .
Structs adalah tipe nilai dan kelas adalah tipe referensi .
Perbedaan umum adalah bahwa tipe referensi tinggal di heap, dan tipe nilai tinggal inline, yaitu, di mana pun itu variabel Anda atau bidang didefinisikan.
Variabel yang berisi tipe nilai berisi seluruh nilai tipe nilai. Untuk struct, itu berarti bahwa variabel berisi seluruh struct, dengan semua bidangnya.
Variabel yang berisi tipe referensi berisi pointer, atau referensi ke tempat lain di memori di mana nilai aktual berada.
Ini memiliki satu manfaat, untuk mulai dengan:
Secara internal, tipe referensi diimplementasikan sebagai pointer, dan mengetahui itu, dan mengetahui bagaimana tugas variabel bekerja, ada pola perilaku lainnya:
Saat Anda mendeklarasikan variabel atau bidang, inilah perbedaan kedua jenis:
Ringkasan masing-masing:
Kelas Saja:
Hanya Structs:
Kelas dan Struktur:
c# struct memory overhead
dan menemukan jawaban ini oleh Hans Passant yang mengatakan bahwa tidak, itu juga tidak benar. Jadi apa yang Anda maksud?
class
dikelola memori (ditangani oleh pengumpul sampah), sedangkan contoh struct
tidak .
Dalam .NET deklarasi struct dan kelas membedakan antara tipe referensi dan tipe nilai.
Ketika Anda melewati jenis referensi hanya ada satu yang benar-benar disimpan. Semua kode yang mengakses instance sedang mengakses yang sama.
Ketika Anda melewati suatu tipe nilai masing-masing adalah salinan. Semua kode bekerja pada salinannya sendiri.
Ini dapat ditunjukkan dengan contoh:
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.
Untuk kelas ini akan berbeda
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.
Kelas bisa berupa apa saja - referensi dapat menunjuk ke nol.
Struct adalah nilai aktual - mereka bisa kosong tetapi tidak pernah nol. Karena alasan ini, struct selalu memiliki konstruktor default tanpa parameter - mereka memerlukan 'nilai awal'.
Perbedaan antara Structs dan Classes:
Dari Microsoft Memilih Antara Kelas dan Struktur ...
Sebagai aturan praktis, sebagian besar jenis dalam suatu kerangka kerja harus kelas. Namun, ada beberapa situasi di mana karakteristik tipe nilai membuatnya lebih tepat untuk menggunakan struct.
✓ PERTIMBANGKAN struct bukan kelas:
- Jika instance dari tipe kecil dan umumnya berumur pendek atau umumnya tertanam di objek lain.
X HINDARI struct kecuali tipe tersebut memiliki semua karakteristik berikut:
- Secara logis mewakili nilai tunggal, mirip dengan tipe primitif (int, dobel, dll.).
- Ini memiliki ukuran instance di bawah 16 byte.
- Itu tidak berubah. (tidak bisa diubah)
- Itu tidak harus sering kotak.
Selain semua perbedaan yang dijelaskan dalam jawaban lain:
Jika Anda mencari video yang menjelaskan semua perbedaan, Anda dapat melihat Bagian 29 - C # Tutorial - Perbedaan antara kelas dan struct di C # .
Contoh kelas disimpan di heap yang dikelola. Semua variabel 'mengandung' instance hanyalah referensi ke instance pada heap. Melewati objek ke metode menghasilkan salinan referensi yang diteruskan, bukan objek itu sendiri.
Struktur (secara teknis, tipe nilai) disimpan di mana pun mereka digunakan, seperti tipe primitif. Konten dapat disalin oleh runtime kapan saja dan tanpa meminta copy-constructor yang disesuaikan. Melewati tipe nilai ke metode melibatkan menyalin seluruh nilai, lagi-lagi tanpa meminta kode yang dapat disesuaikan.
Perbedaannya dibuat lebih baik dengan nama C ++ / CLI: "kelas ref" adalah kelas seperti yang dijelaskan pertama, "kelas nilai" adalah kelas seperti yang dijelaskan kedua. Kata kunci "kelas" dan "struct" seperti yang digunakan oleh C # hanyalah sesuatu yang harus dipelajari.
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| | 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 |
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
Struktur vs Kelas
Struktur adalah tipe nilai sehingga disimpan di tumpukan, tetapi kelas adalah tipe referensi dan disimpan di heap.
Struktur tidak mendukung warisan, dan polimorfisme, tetapi kelas mendukung keduanya.
Secara default, semua anggota struct adalah publik tetapi anggota kelas secara default bersifat pribadi.
Karena struktur adalah tipe nilai, kami tidak dapat menetapkan null ke objek struct, tetapi itu bukan kasus untuk kelas.
Untuk menambah jawaban lain, ada satu perbedaan mendasar yang perlu diperhatikan, dan itu adalah bagaimana data disimpan dalam array karena ini dapat memiliki efek besar pada kinerja.
Jadi array struct terlihat seperti ini di memori
[struct][struct][struct][struct][struct][struct][struct][struct]
Sedangkan array kelas terlihat seperti ini
[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]
Dengan array kelas, nilai yang Anda minati tidak disimpan dalam array, tetapi di tempat lain di memori.
Untuk sebagian besar aplikasi perbedaan ini tidak terlalu penting, namun, dalam kode kinerja tinggi ini akan mempengaruhi lokalitas data dalam memori dan memiliki dampak besar pada kinerja cache CPU. Menggunakan kelas ketika Anda bisa / seharusnya menggunakan struct akan secara besar-besaran meningkatkan jumlah cache yang gagal pada CPU.
Hal paling lambat yang dilakukan CPU modern adalah bukan angka-angka, itu mengambil data dari memori, dan hit cache L1 berkali-kali lebih cepat daripada membaca data dari RAM.
Berikut beberapa kode yang dapat Anda uji. Di mesin saya, iterasi melalui array kelas membutuhkan ~ 3x lebih lama dari array struct.
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}");
}
Hanya untuk membuatnya lengkap, ada perbedaan lain ketika menggunakan Equals
metode ini, yang diwarisi oleh semua kelas dan struktur.
Katakanlah kita memiliki kelas dan struktur:
class A{
public int a, b;
}
struct B{
public int a, b;
}
dan dalam metode Utama, kami memiliki 4 objek.
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;
}
Kemudian:
s1.Equals(s2) // true
s1.Equals(c1) // false
c1.Equals(c2) // false
c1 == c2 // false
Jadi , struktur cocok untuk objek seperti numerik, seperti titik (simpan koordinat x dan y). Dan kelas cocok untuk orang lain. Bahkan jika 2 orang memiliki nama, tinggi, berat yang sama ..., mereka masih 2 orang.
Nah, untuk permulaan, sebuah struct dilewatkan oleh nilai daripada oleh referensi. Structs baik untuk struktur data yang relatif sederhana, sementara kelas memiliki lebih banyak fleksibilitas dari sudut pandang arsitektur melalui polimorfisme dan pewarisan.
Orang lain mungkin bisa memberi Anda lebih banyak detail daripada saya, tetapi saya menggunakan struct ketika struktur yang saya gunakan sederhana.
Selain perbedaan dasar dari specifier akses, dan beberapa yang disebutkan di atas saya ingin menambahkan beberapa perbedaan utama termasuk beberapa yang disebutkan di atas dengan sampel kode dengan output, yang akan memberikan ide yang lebih jelas tentang referensi dan nilai
Structs:
Kelas:
Contoh kode
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;
}
}
Keluaran
Nilai awal dari Struct Object adalah: 10
Metode Inside Struct Nilai Inside Method dari Struct Object adalah: 20
Setelah nilai panggilan Metode Objek Struct adalah: 10
Nilai awal Obyek Kelas adalah: 10
Metode Inside Class Nilai Inside Method dari Class Object adalah: 20
Nilai panggilan Metode Setelah Kelas Obyek adalah: 20
Di sini Anda dapat dengan jelas melihat perbedaan antara panggilan berdasarkan nilai dan panggilan dengan referensi.
Acara yang dideklarasikan di kelas memiliki akses + = dan - = mereka secara otomatis dikunci melalui kunci (ini) untuk membuatnya aman (acara statis dikunci pada tipe kelas). Acara yang dideklarasikan dalam struct tidak memiliki akses + = dan - = mereka yang dikunci secara otomatis. Kunci (ini) untuk struct tidak akan berfungsi karena Anda hanya bisa mengunci pada ekspresi tipe referensi.
Membuat instance struct tidak dapat menyebabkan pengumpulan sampah (kecuali jika konstruktor secara langsung atau tidak langsung membuat instance tipe referensi) sedangkan membuat instance tipe referensi dapat menyebabkan pengumpulan sampah.
Sebuah struct selalu memiliki konstruktor default publik bawaan.
class DefaultConstructor
{
static void Eg()
{
Direct yes = new Direct(); // Always compiles OK
InDirect maybe = new InDirect(); // Compiles if constructor exists and is accessible
//...
}
}
Ini berarti bahwa struct selalu instantiable sedangkan kelas mungkin tidak karena semua konstruktornya bisa pribadi.
class NonInstantiable
{
private NonInstantiable() // OK
{
}
}
struct Direct
{
private Direct() // Compile-time error
{
}
}
Str tidak dapat memiliki destruktor. Sebuah destructor hanyalah sebuah override objek. Finalisasi dalam penyamaran, dan struct, sebagai tipe nilai, tidak dikenakan pengumpulan sampah.
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 secara implisit disegel, kelas tidak.
Sebuah struct tidak bisa abstrak, sebuah kelas bisa.
Sebuah struct tidak dapat memanggil: base () dalam konstruktornya sedangkan kelas tanpa kelas dasar eksplisit dapat.
Sebuah struct tidak dapat memperluas kelas lain, sebuah kelas bisa.
Sebuah struct tidak dapat mendeklarasikan anggota yang dilindungi (misalnya, bidang, tipe bersarang) sebuah kelas bisa.
Sebuah struct tidak dapat mendeklarasikan anggota fungsi abstrak, sebuah kelas abstrak bisa.
Sebuah struct tidak dapat mendeklarasikan anggota fungsi virtual, sebuah kelas dapat.
Sebuah struct tidak dapat mendeklarasikan anggota fungsi yang disegel, sebuah kelas dapat.
Sebuah struct tidak bisa mendeklarasikan override fungsi anggota, sebuah kelas bisa.
Satu-satunya pengecualian untuk aturan ini adalah bahwa struct dapat menimpa metode virtual System.Object, viz, Equals (), dan GetHashCode (), dan ToString ().
Object
, yang akan menyimpan referensi ke salinan kotak dari struct.
Seperti yang disebutkan sebelumnya: Kelas adalah tipe referensi sedangkan Structs adalah tipe nilai dengan semua konsekuensinya.
Sebagai pedoman praktis, Pedoman Desain Kerangka merekomendasikan penggunaan Struct daripada kelas jika:
Ada satu kasus menarik dari teka-teki "kelas vs struct" - situasi ketika Anda harus mengembalikan beberapa hasil dari metode ini: pilih yang akan digunakan. Jika Anda tahu cerita ValueTuple - Anda tahu bahwa ValueTuple (struct) ditambahkan karena itu harus lebih efektif daripada Tuple (kelas). Tapi apa artinya angka? Dua tes: satu adalah struct / kelas yang memiliki 2 bidang, lainnya dengan struct / kelas yang memiliki 8 bidang (dengan dimensi lebih dari 4 - kelas harus menjadi lebih efektif daripada struct dalam hal kutu prosesor, tetapi tentu saja beban GC juga harus dipertimbangkan ).
PS Tolok ukur lain untuk kasus khusus 'kokoh atau kelas dengan koleksi' ada di sana: 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 |
Tes kode:
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;
}
}
}
}
Struct adalah nilai aktual - mereka bisa kosong tetapi tidak pernah nol
Ini benar, namun juga perhatikan bahwa pada. NET 2 struct mendukung versi Nullable dan C # memasok beberapa gula sintaksis untuk membuatnya lebih mudah digunakan.
int? value = null;
value = 1;
(object)(default(int?)) == null
yang tidak dapat Anda lakukan dengan jenis nilai lain, karena ada lebih dari sekadar gula yang terjadi di sini. Gula hanya int?
untuk Nullable<int>
.
Setiap variabel atau bidang tipe nilai primitif atau tipe struktur memiliki contoh unik dari tipe itu, termasuk semua bidangnya (publik dan pribadi). Sebaliknya, variabel atau bidang tipe referensi dapat dianggap nol, atau dapat merujuk ke suatu objek, disimpan di tempat lain, di mana sejumlah referensi lain mungkin juga ada. Bidang struct akan disimpan di tempat yang sama dengan variabel atau bidang tipe struktur itu, yang mungkin berada di tumpukan atau mungkin menjadi bagian dari objek tumpukan lain.
Membuat variabel atau bidang tipe nilai primitif akan membuatnya dengan nilai default; membuat variabel atau bidang tipe struktur akan membuat contoh baru, membuat semua bidang di dalamnya secara default. Membuat instance baru dari jenis referensi akan mulai dengan membuat semua bidang di dalamnya secara default, dan kemudian menjalankan kode tambahan opsional tergantung pada jenisnya.
Menyalin satu variabel atau bidang dari tipe primitif ke yang lain akan menyalin nilainya. Menyalin satu variabel atau bidang tipe struktur ke yang lain akan menyalin semua bidang (publik dan pribadi) dari contoh sebelumnya ke contoh terakhir. Menyalin satu variabel atau bidang tipe referensi ke yang lain akan menyebabkan yang terakhir merujuk ke instance yang sama dengan yang sebelumnya (jika ada).
Penting untuk dicatat bahwa dalam beberapa bahasa seperti C ++, perilaku semantik suatu tipe tidak tergantung pada bagaimana ia disimpan, tetapi itu tidak berlaku untuk .NET. Jika suatu tipe mengimplementasikan semantik nilai yang dapat diubah, menyalin satu variabel dari tipe itu ke yang lain menyalin properti dari instance pertama ke yang lain, dirujuk oleh yang kedua, dan menggunakan anggota dari yang kedua untuk bermutasi akan menyebabkan instance kedua tersebut diubah , tapi bukan yang pertama. Jika suatu tipe mengimplementasikan semantik referensi yang dapat berubah, menyalin satu variabel ke variabel lain dan menggunakan anggota yang kedua untuk bermutasi objek akan mempengaruhi objek yang dirujuk oleh variabel pertama; tipe dengan semantik yang tidak berubah tidak memungkinkan mutasi, jadi tidak masalah secara semantik apakah penyalinan membuat instance baru atau membuat referensi lain ke yang pertama.
Di .NET, dimungkinkan untuk tipe nilai untuk mengimplementasikan semantik di atas, asalkan semua bidang mereka dapat melakukan hal yang sama. Namun, tipe referensi hanya dapat menerapkan semantik referensi yang bisa berubah atau semantik yang tidak dapat diubah; tipe nilai dengan bidang tipe referensi yang bisa berubah terbatas baik untuk mengimplementasikan semantik referensi yang dapat diubah atau semantik hibrid yang aneh.