
Mari kita lihat apa yang dikatakan blog tentang perubahan C # 8.0 yang akan datang ini (Visual Studio 2019 Preview 2 versi):
“Struktur hanya tumpukan muncul di C # 7.2. Mereka sangat berguna, tetapi pada saat yang sama penggunaannya terkait erat dengan pembatasan, misalnya, ketidakmampuan untuk mengimplementasikan antarmuka. Sekarang struktur tautan dapat dibersihkan menggunakan metode Buang di dalamnya tanpa menggunakan antarmuka IDisposable. "
Jadi begitulah: struktur ref stack-only tidak mengimplementasikan antarmuka, jika tidak, kemungkinan kemasannya akan muncul. Oleh karena itu, mereka tidak dapat menerapkan IDisposable, dan kami tidak dapat menggunakan struktur ini dalam pernyataan menggunakan:
class Program { static void Main(string[] args) { using (var book = new Book()) { Console.WriteLine("Hello World!"); } } } ref struct Book : IDisposable { public void Dispose() { } }
Mencoba menjalankan kode ini akan menghasilkan kesalahan kompilasi :
Error CS8343 'Book': ref structs cannot implement interfaces
Namun, sekarang jika kita menambahkan metode Dispose
publik ke struktur referensi, pernyataan using
akan menerimanya secara ajaib dan semuanya akan dikompilasi:
class Program { static void Main(string[] args) { using (var book = new Book()) { // ... } } } ref struct Book { public void Dispose() { } }
Selain itu, berkat perubahan dalam pernyataan itu sendiri, Anda sekarang dapat menggunakan menggunakan dalam bentuk yang lebih pendek (yang disebut using
deklarasi):
class Program { static void Main(string[] args) { using var book = new Book(); // ... } }
Tapi ... kenapa?
Ini adalah cerita yang panjang, tetapi secara umum, pembersihan eksplisit (finalisasi deterministik) lebih disukai daripada implisit (finalisasi non-deterministik). Ini intuitif. Lebih baik untuk secara eksplisit menghapus sumber daya sesegera mungkin (dengan memanggil Tutup, Buang, atau menggunakan pernyataan), daripada menunggu pembersihan implisit yang akan terjadi "suatu hari" (ketika lingkungan itu sendiri memulai finalizer).
Karena itu, saat membuat jenis yang memiliki sumber daya tertentu, lebih baik menyediakan kemungkinan pembersihan secara eksplisit. Dalam C #, ini jelas antarmuka IDisposable
dan metode Dispose
.
Catatan Jangan lupa bahwa dalam kasus struktur referensi, hanya pembersihan eksplisit yang digunakan, karena definisi finalizer untuk mereka tidak mungkin.
Mari kita lihat contoh ilustratif dari "pembungkus memori yang tidak terkelola". Ini menempati ruang sekecil mungkin (tumpukan tidak digunakan sama sekali) justru berkat struktur tautan yang ditujukan untuk orang yang terobsesi dengan kinerja:
public unsafe ref struct UnmanagedArray<T> where T : unmanaged { private T* data; public UnmanagedArray(int length) { data = // get memory from some pool } public ref T this[int index] { get { return ref data[index]; } } public void Dispose() { // return memory to the pool } }
Karena pembungkus berisi sumber daya yang tidak dikelola, kami menggunakan metode Buang untuk membersihkannya setelah digunakan. Jadi contohnya terlihat seperti ini:
static void Main(string[] args) { var array = new UnmanagedArray<int>(10); Console.WriteLine(array[0]); array.Dispose(); }
Ini tidak nyaman karena Anda harus ingat tentang menelepon Buang. Juga, ini adalah keputusan yang menyakitkan, karena menangani pengecualian dengan benar tidak berlaku di sini. Oleh karena itu, agar Buang dipanggil dari dalam, pernyataan penggunaan diperkenalkan. Namun, sebelumnya, seperti yang telah disebutkan, tidak mungkin untuk menerapkannya dalam situasi ini.
Namun dalam C # 8.0, Anda dapat memanfaatkan sepenuhnya pernyataan menggunakan:
static void Main(string[] args) { using (var array = new UnmanagedArray<int>(10)) { Console.WriteLine(array[0]); } }
Pada saat yang sama, kode menjadi lebih ringkas berkat deklarasi:
static void Main(string[] args) { using var array = new UnmanagedArray<int>(10); Console.WriteLine(array[0]); }
Dua contoh lain di bawah ini (sebagian besar kode dihilangkan untuk singkatnya) diambil dari repositori CoreFX.
Contoh pertama adalah struktur referensi ValueUtf8Converter, yang membungkus array byte [] dari kumpulan array:
internal ref struct ValueUtf8Converter { private byte[] _arrayToReturnToPool; ... public ValueUtf8Converter(Span<byte> initialBuffer) { _arrayToReturnToPool = null; } public Span<byte> ConvertAndTerminateString(ReadOnlySpan<char> value) { ... } public void Dispose() { byte[] toReturn = _arrayToReturnToPool; if (toReturn != null) { _arrayToReturnToPool = null; ArrayPool<byte>.Shared.Return(toReturn); } } }
Contoh kedua adalah RegexWriter, yang membungkus dua struktur referensi ValueListBuilder yang perlu dihapus secara eksplisit (karena mereka juga mengelola array dari kumpulan array):
internal ref struct RegexWriter { ... private ValueListBuilder<int> _emitted; private ValueListBuilder<int> _intStack; ... public void Dispose() { _emitted.Dispose(); _intStack.Dispose(); } }
Kesimpulan
Struktur referensi yang dapat dilepas dapat dianggap sebagai tipe ruang rendah yang memiliki destruktor NYATA, seperti dalam C ++. Itu akan dipanggil segera setelah instance terkait melampaui lingkup pernyataan using (atau lingkup dalam kasus deklarasi use).
Tentu saja, mereka tidak akan tiba-tiba menjadi populer ketika menulis program reguler, berorientasi komersial, tetapi jika Anda membuat kode tingkat tinggi berkinerja tinggi, Anda harus mengetahuinya.
Dan kami juga memiliki artikel tentang konferensi kami:
