المزيد من الميزات مع أنماط في C # 8.0

في الآونة الأخيرة ، تم إصدار Visual Studio 2019 Preview 2. ومعه ، هناك عدد من ميزات C # 8.0 الإضافية جاهزة لتجربتها. هذه بشكل أساسي مقارنة بالعينة ، لكن في النهاية سأتطرق إلى بعض الأخبار والتغييرات الأخرى.


هذه المقالة باللغة الإنجليزية.




شكرًا لك على ترجمة MSP ، Lev Bulanov .

المزيد من الأنماط في المزيد من الأماكن


عندما ظهرت مطابقة النمط في الإصدار C # 7.0 ، لاحظنا أنه من المتوقع حدوث زيادة في عدد الأنماط في أماكن أكثر في المستقبل. لقد حان هذا الوقت! إننا نضيف ما نسميه أنماطًا متكررة ، بالإضافة إلى شكل أكثر تعبيرًا من تعبيرات المحولات تسمى تعبيرات التبديل.


للبدء ، إليك مثال بسيط على أنماط C # 7.0:


class Point { public int X { get; } public int Y { get; } public Point(int x, int y) => (X, Y) = (x, y); public void Deconstruct(out int x, out int y) => (x, y) = (X, Y); } static string Display(object o) { switch (o) { case Point p when pX == 0 && pY == 0: return "origin"; case Point p: return $"({pX}, {pY})"; default: return "unknown"; } } 

تبديل التعبيرات


أولاً ، لاحظ أن العديد من تعبيرات المحولات ، في الواقع ، لا تفعل الكثير من العمل المثير للاهتمام في الهيئات. غالبًا ما ينشئ كل منهم قيمة ، إما عن طريق تعيينها لمتغير أو بإعادتها (كما هو موضح أعلاه). في كل هذه الحالات ، يبدو أن المفتاح غير صالح. هذا يشبه ميزة اللغة التي تبلغ من العمر خمسين عامًا.


قررنا أن الوقت قد حان لإضافة نموذج بيان التبديل . ينطبق على المثال أدناه:


 static string Display(object o) { return o switch { Point p when pX == 0 && pY == 0 => "origin", Point p => $"({pX}, {pY})", _ => "unknown" }; } 

هناك بعض الأشياء التي تغيرت مقارنةً ببيانات التبديل. دعنا نذكرهم:


  • الكلمة الأساسية للتبديل هي "infix" بين القيمة المختبرة وقائمة الحالات {...} . هذا يجعله أكثر تركيبية مع التعبيرات الأخرى ، ويسهل تمييزه بصريًا عن بيان التبديل.
  • الكلمة الرئيسية للحالة والرمز: تم استبدالها بسهم lambda => لفترة قصيرة.
  • تم استبدال الإعداد الافتراضي للإيجاز بنمط إعادة التعيين.
  • الهيئات هي التعبيرات. تصبح نتيجة النص المحدد نتيجة بيان التبديل.

نظرًا لأن التعبير يجب أن يكون مهمًا أو يطرح استثناءًا ، فإن تعبير التحديد الذي ينتهي بدون تطابق سيؤدي إلى استثناء. سيقوم المحول البرمجي بتحذيرك عندما يحدث هذا ، لكنه لن يفرض عليك إنهاء جميع عبارات التحديد بوظيفة catch-all.


نظرًا لأن طريقة العرض لدينا تتكون الآن من عبارة إرجاع مفردة ، يمكننا تبسيطها للتعبير:


  static string Display(object o) => o switch { Point p when pX == 0 && pY == 0 => "origin", Point p => $"({pX}, {pY})", _ => "unknown" }; 

مهما كانت توصيات التنسيق المقدمة ، يجب أن تكون واضحة وموجزة للغاية. يسمح لك Brevity بتنسيق المفتاح بطريقة "مجدولة" ، كما هو موضح أعلاه ، مع وجود أنماط و bod على نفس السطر ، و => تصطف تحت بعضها البعض.


بالمناسبة ، نخطط للسماح باستخدام فاصلة بعد الحالة الأخيرة وفقًا لجميع "القوائم الأخرى مفصولة بفواصل بين قوسين مجعدين" في C # ، لكن هذا غير مسموح به بعد في المعاينة 2.


خصائص النمط


الحديث عن الإيجاز ، أصبحت الأنماط فجأة أصعب العناصر في تعبيرات الاختيار. دعونا نفعل شيئا حيال ذلك.


لاحظ أن تعبير التحديد يستخدم نمطًا من النوع Point p (مرتين) ، وكذلك عند إضافة شروط إضافية في الحالة الأولى.


في C # 8.0 ، نضيف عناصر اختيارية إضافية إلى نوع الأنماط ، مما يسمح للنموذج نفسه بالعمق أكثر في القيمة التي تطابق النموذج. يمكنك جعله نمط خاصية عن طريق إضافة {...} يحتوي على أنماط متداخلة ، وتطبيق القيم على الخصائص أو الحقول المتاحة. هذا يسمح لنا بإعادة كتابة تعبير التبديل كما يلي:


 static string Display(object o) => o switch { Point { X: 0, Y: 0 } p => "origin", Point { X: var x, Y: var y } p => $"({x}, {y})", _ => "unknown" }; 

كلتا الحالتين ما زالت تحقق من أن o هي النقطة . في الحالة الأولى ، يتم تطبيق نمط الثابت 0 بشكل متكرر على الخواص X و Y للمتغير p ، والتحقق مما إذا كانت هذه القيمة موجودة أم لا. وبالتالي ، يمكننا القضاء على حالة الشرط في هذه الحالات وغيرها من الحالات المماثلة.


في الحالة الثانية ، يتم تطبيق نمط var على كل من X و Y. تذكر أن نمط var في C # 7.0 ينجح دائمًا ويعلن ببساطة عن متغير جديد للاحتفاظ بالقيمة. لذا فإن x و y تحتويان على قيم int ل pX و pY .


نحن لا نستخدم p مطلقًا ويمكننا تخطيها هنا:


  Point { X: 0, Y: 0 } => "origin", Point { X: var x, Y: var y } => $"({x}, {y})", _ => "unknown" 

يبقى شيء واحد هو نفسه بالنسبة لجميع أنواع الأنماط ، بما في ذلك أنماط الخصائص ، وهذا مطلب بأن تكون القيمة غير صفرية. هذا يفتح إمكانية استخدام نمط الخصائص "الفارغة" {} كنموذج مضغوط "غير صفري". على سبيل المثال يمكننا استبدال الاستعاضة بالحالتين التاليتين:


  {} => o.ToString(), null => "null" 

{} يتعامل مع الكائنات غير الصفرية المتبقية ، وتحصل القيمة الخالية على أصفار ، وبالتالي فإن رمز التبديل شامل ، ولن يشكو المترجم من القيم المفقودة.


أنماط الموقف


لا يختصر نمط الخاصية حالة النقطة الثانية . ليست هناك حاجة للقلق بشأن هذا ، يمكنك أن تفعل أكثر من ذلك.


لاحظ أن الفئة Point لها طريقة Deconstruct ، تسمى deconstructor. في C # 7.0 ، يسمح لك deconstructors بـ "deconstruct" بقيمة عند التعيين ، بحيث يمكنك الكتابة ، على سبيل المثال:


 (int x, int y) = GetPoint(); // split up the Point according to its deconstructor 

لم C # 7.0 دمج تفكيك مع الأنماط. هذا يتغير مع أنماط الموضعية ، والتي هي وسيلة إضافية لتوسيع أنواع الأنماط في C # 8.0. إذا كان النوع المطابق من النوع tuple أو كان يحتوي على مُنحل ، فيمكننا استخدام الأنماط الموضعية كوسيلة مدمجة لتطبيق الأنماط العودية دون الحاجة إلى تسمية الخصائص:


 static string Display(object o) => o switch { Point(0, 0) => "origin", Point(var x, var y) => $"({x}, {y})", _ => "unknown" }; 

بعد مطابقة الكائن مع Point ، يتم تطبيق deconstructor ، ويتم تطبيق الأنماط المتداخلة على القيم الناتجة.


تفكيك المباني ليست دائما مناسبة. يجب إضافتها فقط إلى تلك الأنواع حيث يكون من الواضح حقًا أي القيم هي. على سبيل المثال ، بالنسبة لفئة Point ، يمكنك افتراض أن القيمة الأولى هي X والثانية هي Y ، لذلك فإن تعبير التبديل أعلاه واضح وسهل القراءة.


أنماط Tuple


وهناك حالة خاصة مفيدة للغاية من أنماط الموضعية هي تطبيقها على tuples. إذا تم تطبيق عبارة التبديل مباشرةً على تعبير tuple ، فنحن نسمح أيضًا بحذف المجموعة الإضافية من الأقواس ، كما هو الحال في التبديل (x ، y ، z) بدلاً من التبديل ((x ، y ، z)) .


تعد أنماط Tuple رائعة لاختبار المدخلات المتعددة في نفس الوقت. هنا تطبيق بسيط لجهاز الدولة:


 static State ChangeState(State current, Transition transition, bool hasKey) => (current, transition) switch { (Opened, Close) => Closed, (Closed, Open) => Opened, (Closed, Lock) when hasKey => Locked, (Locked, Unlock) when hasKey => Closed, _ => throw new InvalidOperationException($"Invalid transition") }; 

بالطبع ، يمكننا تضمين hasKey في المجموعة بدلاً من استخدام الجمل - وهذه مسألة ذوق:


 static State ChangeState(State current, Transition transition, bool hasKey) => (current, transition, hasKey) switch { (Opened, Close, _) => Closed, (Closed, Open, _) => Opened, (Closed, Lock, true) => Locked, (Locked, Unlock, true) => Closed, _ => throw new InvalidOperationException($"Invalid transition") }; 

بشكل عام ، ترى أن الأنماط العودية وعبارات التبديل يمكن أن تؤدي إلى منطق برنامج أكثر وضوحًا وتوضيحًا.


ميزات أخرى لـ C # 8.0 في المعاينة 2


على الرغم من حقيقة أن المهام الرئيسية للعمل مع الأنماط في VS 2019 Preview 2 هي الأكثر أهمية ، فهناك العديد من الوظائف الأصغر التي آمل أن تجدها مفيدة ومثيرة للاهتمام. لن أخوض في التفاصيل ، فقط أعطِ وصفًا موجزًا ​​لكل منها.


باستخدام الإعلانات


في استخدام C # ، يعمل المشغلون دائمًا على زيادة مستوى التعشيش ، مما قد يكون مزعجًا جدًا وقراءة سيئة. في الحالات البسيطة ، عندما تحتاج فقط إلى مسح المورد في نهاية النطاق ، يتم استخدام التعريفات. إن استخدام التعريفات هو مجرد إعلانات للمتغيرات المحلية مع استخدام الكلمة الأساسية أمامها ، ويتم وضع محتوياتها في نهاية مجموعة التعليمات الحالية. لذلك ، بدلاً من:

 static void Main(string[] args) { using (var options = Parse(args)) { if (options["verbose"]) { WriteLine("Logging..."); } ... } // options disposed here } 

يمكنك فقط الكتابة


 static void Main(string[] args) { using var options = Parse(args); if (options["verbose"]) { WriteLine("Logging..."); } } // options disposed here 

هياكل المرجع المتاح


تم تقديم هياكل Ref في C # 7.2 ، ويبدو أنه لا يوجد مكان للتكرار عنها هنا. ومع ذلك ، تجدر الإشارة إلى شيء ما: لديهم بعض القيود ، مثل استحالة تنفيذ الواجهات. يمكن الآن استخدام بنيات المرجع دون تطبيق واجهة IDisposable ، ببساطة باستخدام طريقة التخلص منها.


وظائف محلية ثابتة


إذا كنت تريد التأكد من أن وظيفتك المحلية لا تتحمل تكاليف وقت التشغيل المرتبطة بـ "التقاط" (الرجوع إلى) المتغيرات من النطاق ، يمكنك إعلانها على أنها ثابتة. بعد ذلك سيمنع المترجم الارتباط بكل شيء يتم الإعلان عنه في الدالات المغلقة - باستثناء الوظائف المحلية الثابتة الأخرى!


التغييرات من المعاينة 1


كانت الوظائف الرئيسية للمعاينة 1 أنواع مرجعية لاغية وتدفقات غير متزامنة. تغيرت كلتا الميزتين بعض الشيء في المعاينة 2 ، لذلك إذا بدأت استخدامهما ، فمن المفيد معرفة ما يلي.


أنواع المرجع لاغية


لقد أضفنا المزيد من الخيارات لإدارة تحذيرات nullable في المصدر (عبر توجيهات التحذير #nullable و # pragma ) وعلى مستوى المشروع. لقد قمنا أيضًا بتغيير الاشتراك في ملف المشروع إلى <NullableContextOptions> تمكين </ NullableContextOptions> .


المواضيع غير متزامن


لقد قمنا بتغيير شكل واجهة IAsyncEnumerable <T> التي يتوقعها المترجم. هذا يؤدي إلى حقيقة أن المترجم لا يتزامن مع الواجهة المتوفرة في .NET Core 3.0 Preview 1 ، والتي قد تسبب بعض المشاكل. ومع ذلك ، سيتم إصدار .NET Core 3.0 Preview 2 قريبًا وسيؤدي ذلك إلى إرجاع المزامنة.

Source: https://habr.com/ru/post/ar440530/


All Articles