ديناميكي في C #: وصفات الاستخدام

هذا هو الجزء الأخير من سلسلة وقت تشغيل اللغة الديناميكية . المقالات السابقة:

  1. التفاصيل الديناميكية: ألعاب المترجم السري ، تسرب الذاكرة ، الفروق الدقيقة في الأداء . تتناول هذه المقالة ذاكرة التخزين المؤقت DLR بالتفصيل والنقاط المهمة للمطور.
  2. Grokl DLR . نظرة عامة حول التقنية ، وتشريح DynamicMetaObject ، وإرشادات قصيرة حول كيفية إنشاء فئة ديناميكية خاصة بك.

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



عندما لا غنى عن الديناميكية


لا توجد مثل هذه الحالات. يمكنك دائمًا كتابة رمز مشابه في الوظائف بأسلوب ثابت ، والفرق الوحيد هو سهولة القراءة ومقدار الشفرة. على سبيل المثال ، عند العمل مع كائنات COM ، بدلاً من الديناميكي ، يمكنك استخدام الانعكاس.

عندما الديناميكي مفيد


العمل مع كائنات COM


بادئ ذي بدء ، هذا ، بالطبع ، هو العمل مع كائنات COM ، من أجل هذا كله بدأ. قارن الكود الذي تم الحصول عليه مع الديناميكي والتفكير:

dynamic instance = Activator.CreateInstance(type); instance.Run("Notepad.exe"); 

 var instance = Activator.CreateInstance(type); type.InvokeMember("Run", BindingFlags.InvokeMethod, null, instance, new[] { "Notepad.exe" }); 

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

العمل مع التكوينات


مثال آخر للكتب المدرسية يعمل مع التكوينات ، مثل XML . بدون ديناميكية :

 XElement person = XElement.Parse(xml); Console.WriteLine( $"{person.Descendants("FirstName").FirstOrDefault().Value} {person.Descendants("LastName").FirstOrDefault().Value}" ); 

مع ديناميكية:

 dynamic person = DynamicXml.Parse(xml); Console.WriteLine( $"{person.FirstName} {person.LastName}" ); 

بالطبع ، لهذا تحتاج إلى تنفيذ الفصل الديناميكي الخاص بك. كبديل عن القائمة الأولى ، يمكنك كتابة فصل يعمل بشيء من هذا القبيل:

 var person = StaticXml.Parse(xml); Console.WriteLine( $"{person.GetElement("FirstName")} {person.GetElement("LastName")}" ); 

ولكن ، كما ترى ، يبدو هذا أقل أناقة من خلال ديناميكية .

العمل مع الموارد الخارجية


يمكن تعميم الفقرة السابقة على أي إجراءات بموارد خارجية. لدينا دائمًا بديلان: استخدام الديناميكي للحصول على الكود في نمط C # الأصلي أو الكتابة الثابتة مع "الخطوط السحرية". دعنا ننظر إلى مثال مع طلب REST API . مع ديناميكية ، يمكنك كتابة هذا:

 dynamic dynamicRestApiClient = new DynamicRestApiClient("http://localhost:18457/api"); dynamic catsList = dynamicRestApiClient.CatsList; 

حيث سترسل فئتنا الديناميكية طلب النموذج بناءً على طلب العقار

 [GET] http://localhost:18457/api/catslist 

ثم يقوم بإلغاء تسلسلها ويعيد إلينا مجموعة من القطط الجاهزة بالفعل للاستخدام المقصود. بدون ديناميكية ، ستبدو مثل هذا:

 var restApiClient = new RestApiClient("http://localhost:18457/api"); var catsListJson = restApiClient.Get("catsList"); var deserializedCatsList = JsonConvert.DeserializeObject<Cat[]>(catsListJson); 

استبدال الانعكاس


في المثال السابق ، قد يكون لديك سؤال: لماذا في إحدى الحالات نقوم بإلغاء تحديد قيمة الإرجاع لنوع معين ، وفي الحالة الأخرى لا؟ والحقيقة هي أننا في الكتابة الثابتة نحتاج إلى تحويل الكائنات بوضوح إلى نوع Cat للعمل معها. في حالة الديناميكية ، يكفي إلغاء تسلسل JSON إلى مجموعة من الكائنات داخل صفتنا الديناميكية وإرجاع كائن [] منه ، حيث أن الديناميكية تتولى التفكير. سأقدم مثالين على كيفية عمل هذا:

 dynamic deserialized = JsonConvert.DeserializeObject<object>(serialized); var name = deserialized.Name; var lastName = deserialized.LastName; 

 Attribute[] attributes = type.GetCustomAttributes(false).OfType<Attribute>(); dynamic attribute = attributes.Single(x => x.GetType().Name == "DescriptionAttribute"); var description = attribute.Description; 

نفس المبدأ عند العمل مع كائنات COM.

زائر


باستخدام ديناميكية ، يمكنك تنفيذ هذا النمط بأناقة فائقة. بدلا من ألف كلمة:

 public static void DoSomeWork(Item item) { InternalDoSomeWork((dynamic) item); } private static void InternalDoSomeWork(Item item) { throw new Exception("Couldn't find handler for " + item.GetType()); } private static void InternalDoSomeWork(Sword item) { //do some work with sword } private static void InternalDoSomeWork(Shield item) { //do some work with shield } public class Item { } public class Sword : Item {} public class Shield : Item {} 

الآن ، عند تمرير كائن من النوع Sword إلى طريقة DoSomeWork ، سيتم استدعاء أسلوب InternalDoSomeWork (عنصر السيف) .

النتائج


إيجابيات استخدام الديناميكي :

  • يمكن استخدامه للنماذج الأولية السريعة: في معظم الحالات ، يتناقص عدد كود الشفرة
  • كقاعدة عامة ، فإنه يحسن قابلية القراءة والجمالية (بسبب الانتقال من "الخطوط السحرية" إلى النمط الأصلي للغة) من الكود
  • على الرغم من الرأي السائد ، وبفضل آليات التخزين المؤقت ، لا ينشأ مقدار كبير من الأداء في الحالة العامة

سلبيات استخدام الديناميكي:

  • هناك فروق دقيقة غير واضحة مرتبطة بالذاكرة والأداء.
  • مع دعم وقراءة هذه الفئات الديناميكية ، عليك أن تفهم جيدًا ما يجري
  • المحرم محروم من فحص النوع وجميع الضمانات الصحية التي يقدمها المترجم

استنتاج


في رأيي ، سوف يحصل المطور على أكبر ربح من استخدام الديناميكي في المواقف التالية:

  • عندما النماذج
  • في المشروعات الصغيرة / المنزلية حيث تكون تكلفة الخطأ منخفضة
  • في الأدوات المساعدة صغيرة الحجم التي لا تحتوي على وقت تشغيل طويل. إذا تم تنفيذ الأداة المساعدة في أسوأ الأحوال لبضع ثوانٍ ، فعادةً ما تكون هناك حاجة للتفكير في تسرب الذاكرة وتدهور الأداء

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

إذا كنت تعمل مع كائنات COM أو المجالات في الخدمات / المنتجات التي تنطوي على وقت عمل مستمر طويل ، فمن الأفضل عدم استخدام ديناميكي ، على الرغم من حقيقة أنه تم إنشاؤه لمثل هذه الحالات. حتى إذا كنت تعرف تمامًا ماذا وكيف تفعل ولا تخطئ أبدًا ، فقد يأتي مطور جديد عاجلاً أم آجلاً لا يعرف ذلك. من المحتمل أن تكون النتيجة تسرب للذاكرة يصعب حسابها.

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


All Articles