
دعونا نرى ما تقوله المدونة حول هذا التغيير القادم في الإصدار C # 8.0 (الإصدار 2 من Visual Studio 2019):
ظهرت الهياكل المكدس فقط في C # 7.2. إنها مفيدة للغاية ، ولكن في الوقت نفسه يرتبط استخدامها بشكل وثيق بالقيود ، على سبيل المثال ، عدم القدرة على تنفيذ واجهات. الآن يمكن تنظيف بنيات الارتباط باستخدام طريقة التخلص داخلها دون استخدام واجهة IDisposable. "
لذلك هو: هياكل ref المكدس فقط لا تنفذ واجهات ، وإلا فإن احتمال التعبئة والتغليف سوف تنشأ. لذلك ، لا يمكنهم تطبيق IDisposable ، ولا يمكننا استخدام هذه الهياكل في عبارة الاستخدام:
class Program { static void Main(string[] args) { using (var book = new Book()) { Console.WriteLine("Hello World!"); } } } ref struct Book : IDisposable { public void Dispose() { } }
ستؤدي محاولة تشغيل هذا الرمز إلى حدوث خطأ في الترجمة :
Error CS8343 'Book': ref structs cannot implement interfaces
ومع ذلك ، الآن إذا أضفنا طريقة Dispose
العامة إلى بنية المرجع ، فإن عبارة الاستخدام ستقبلها بطريقة سحرية وسيتم تجميع كل شيء:
class Program { static void Main(string[] args) { using (var book = new Book()) { // ... } } } ref struct Book { public void Dispose() { } }
علاوة على ذلك ، بفضل التغييرات في البيان نفسه ، يمكنك الآن استخدام استخدام في شكل أقصر (ما يسمى using
الإعلانات):
class Program { static void Main(string[] args) { using var book = new Book(); // ... } }
لكن ... لماذا؟
هذه قصة طويلة ، ولكن بشكل عام ، يفضل التنظيف الصريح (الصيغة النهائية الحتمية) ضمنيًا (الصيغة النهائية غير الحتمية). هذا بديهي. من الأفضل مسح الموارد بشكل صريح في أسرع وقت ممكن (عن طريق استدعاء إغلاق أو التخلص أو استخدام العبارة) ، بدلاً من انتظار التنظيف الضمني الذي سيحدث "في يوم من الأيام" (عندما تبدأ البيئة نفسها في وضع اللمسات الأخيرة).
لذلك ، عند إنشاء نوع يمتلك موردًا معينًا ، من الأفضل توفير إمكانية التنظيف بشكل صريح. في C # ، من الواضح أن هذا هو واجهة IDisposable
وطريقة Dispose
.
المذكرة. لا تنسَ أنه في حالة الهياكل المرجعية ، يتم استخدام التنظيف الصريح فقط ، نظرًا لأن تعريف أجهزة الصقل النهائية مستحيل.
دعونا نلقي نظرة على مثال توضيحي "للالتفاف المعتاد لتجمع ذاكرة غير مُدار". إنها تشغل أصغر مساحة ممكنة (لا يتم استخدام الكومة على الإطلاق) على وجه التحديد بفضل بنية الوصلة المخصصة للأشخاص المهووسين بالأداء:
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 } }
نظرًا لأن المجمع يحتوي على مورد غير مدار ، فإننا نستخدم طريقة التخلص لتنظيفه بعد الاستخدام. لذلك يبدو المثال كالتالي:
static void Main(string[] args) { var array = new UnmanagedArray<int>(10); Console.WriteLine(array[0]); array.Dispose(); }
هذا غير مريح لأنك بحاجة إلى أن تتذكر عن استدعاء التخلص. أيضًا ، هذا قرار مؤلم ، لأن معالجة الاستثناءات بشكل صحيح لا ينطبق هنا. لذلك ، لكي يتم استدعاء Dispos من الداخل ، تم تقديم عبارة الاستخدام. ومع ذلك ، في وقت سابق ، كما سبق ذكره ، كان من المستحيل تطبيقه في هذه الحالة.
ولكن في C # 8.0 ، يمكنك الاستفادة الكاملة من عبارة الاستخدام:
static void Main(string[] args) { using (var array = new UnmanagedArray<int>(10)) { Console.WriteLine(array[0]); } }
في الوقت نفسه ، أصبح الرمز أكثر إيجازًا بفضل الإعلانات:
static void Main(string[] args) { using var array = new UnmanagedArray<int>(10); Console.WriteLine(array[0]); }
المثالان الآخران أدناه (تم حذف جزء كبير من الكود للإيجاز) من مستودع CoreFX.
المثال الأول هو هيكل مرجع ValueUtf8Converter ، الذي يلتف صفيف بايت [] من تجمع صفيف:
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); } } }
المثال الثاني هو RegexWriter ، الذي يلتف بنيتين مرجعيتين لـ ValueListBuilder يلزم مسحهما صراحة (نظرًا لأنهما يديران أيضًا صفائف من تجمع الصفيف):
internal ref struct RegexWriter { ... private ValueListBuilder<int> _emitted; private ValueListBuilder<int> _intStack; ... public void Dispose() { _emitted.Dispose(); _intStack.Dispose(); } }
استنتاج
يمكن اعتبار البنى المرجعية القابلة للإزالة من الأنواع ذات المساحة المنخفضة التي تحتوي على مُدمّر حقيقي ، كما في C ++. سيتم التذرع به بمجرد أن يتجاوز المثيل المقابل نطاق عبارة الاستخدام (أو النطاق في حالة إعلان الاستخدام).
بالطبع ، لن تصبح شائعة فجأة عند كتابة برامج منتظمة موجهة نحو التجارة ، ولكن إذا كنت تقوم بإنشاء رمز عالي الأداء ومنخفض المستوى ، فيجب أن تعرف عنها.
ولدينا أيضا مقال عن مؤتمرنا:
