التجريد يعيق استخدام الأنواع المخصصة ، ما هي القواعد التي يجب اتباعها في التنفيذ؟
-
28-09-2019 - |
سؤال
لقد قمت ببناء نظام أنواع مخصص للاستخدام في C# البرمجة النصية داخل التطبيق. يتم تجميع البرامج النصية أثناء التنقل وتسمح بالتفاعل مع البيانات الداخلية للتطبيق. تم تصميم هذا الأنواع بطريقة مجردة باستخدام واجهات مثل IValue
. بداية شئ IValue
يمكن أن يكون RefString
, RefInteger
, RefDouble
(من بين أشياء أخرى كثيرة ، ولكن هذا يكفي لإظهار مشكلتي).
الآن وصلنا إلى النقطة التي أتعثر فيها ... استخدام هذه IValue
الكائنات غير طبيعية إلى حد ما. يُعتبر تصميمًا جيدًا دائمًا استخدام الواجهات للتفاعل مع الكائنات ولكن لا توجد إمكانية لتحديد تحويل ضمني أو تحميل مشغل للواجهات. هذا ينتج عن المواقف التي لا يمكن تجنب الصب الصريح القبيح بحيث يتم استخدام المشغل المناسب.
مثال:
IValue Add(IValue a, IValue b)
{
//return a+b; // won't work: which operator +() to use?
return (RefInteger)a + (RefInteger)b;
}
في حالة C# في التعبيرات التي تنطوي على أنواع القيمة ، يتم توفير تحويلات ضمنية. ما هي الطريقة الجيدة لتصميم مثل هذا النظام المخصص؟
أعيد كتابة نظام الأنواع لإزالة IValue
واجهة وتقديم أ RefValue
فئة قاعدة. بهذه الطريقة يمكن القضاء على جزء من القوالب الصريحة. لقد قمت بتطبيق بعض من المشغل الزائد في هذه الفئة الأساسية ، لكن ذلك تسبب في الكثير من المتاعب مع مشغلي التحويل الافتراضي ... بجانب هذا المنطق الذي ينفذه في تنفيذ المشغل يضمن الكثير من المعرفة حول الأنواع في النظام. أعتقد أن هذا ما زال بطريقة ما هو الطريقة التي يجب أن يذهب بها المرء ولكن ما هي القواعد التي يجب اتباعها ، لتنفيذ هذا بطريقة جيدة وآمنة؟
تعديل: بعد النضال لفترة من الوقت ، بعض القواعد التي يمكنني اكتشافها هي:
- إعلان ضمنيًا فقط مشغلي التحويل من الأنواع الأساسية (int ، مزدوجة ، سلسلة ، ...)
- أعلن صريحًا التحويلات إلى الأنواع الأساسية (لتجنب القوالب الضمنية إلى int !! ماذا يحدث في كثير من الأحيان ، ولكن لماذا؟)
- لتجنب المكالمات الغامضة ، لا ينبغي تجاوز المشغلين +، /، * في الفئة الأساسية وفي الفئات المشتقة. [ما هي السبيل للذهاب بعد ذلك؟ لقد قمت بالعملية الزائدة في الفصول المشتقة ، وهذا يتطلب الاستخدام ، وهو قبيح مرة أخرى ...
المحلول
إذا كان من المفترض أن تكون قادرًا على عمل Add
العملية على الجميع IValue
S ، ربما يجب أن تشمل الواجهة Add
طريقة؟ ثم يمكنك أن تفعل return a.Add(b);
, ، وادفع معرفة كيفية أداء العملية في كل نوع.
مشكلة واحدة هي أنه كما يبدو الآن ، يمكنك الحصول على مكالمات حيث a
هو RefString
و b
هو RefInteger
, ، ربما ليس ما تريد. يمكن أن تساعد الأدوية في إصلاح ذلك:
T Add<T>(T a, T b) where T : IValue
{
return a.Add(b);
}
(بالطبع ستحتاج إلى إضافة فحص فارغ ومثل)