فخ واجهة دلفي
الآن أعرف مخيطتين رئيسيتين:
فخ 1. فخ نوع الواجهة
أ) لا يمكنك إلقاء مرجع كائن إلى واجهة لا تعلن نوع هذا المرجع ، حتى لو كان الكائن ينفذ هذه الواجهة فعليًا (من الصعب نطق المزايا). ب) عندما يتم تعيين متغير كائن إلى متغير واجهة ، وعندما يتم تعيين متغير الواجهة إلى متغير الكائن ، تغير عنوان متغير الكائن ، أي أنه لم يعد الكائن الأصلي ، ولكنه يشير إلى عنوان خطأ . على سبيل المثال: i1 = واجهة
وظيفة تفعل: منطقية ؛
نهاية؛
TC1 = الفصل
ATT1: عدد صحيح ؛
نهاية؛
TC2 = الفصل (TC1 ، I1)
ATT2: عدد صحيح ؛
وظيفة تفعل: منطقية ؛
End ؛ intf1: i1 ؛ obj1: tc! ؛ obj2: tc2 ؛ obj2: = tc2.create ؛
OBJ1: = OBJ2.
I1 (OBJ2).
I1 (OBJ1). نظرًا لأن نوع OBJ1 TC1 لا يعلن أنه تم تنفيذ I1 ، فلا يمكن تحويله إلى I1 ، حتى لو كان OBJ1 ينفذ I1. أيضًا ، إذا قمت بتحويل الكائن إلى واجهة ثم العودة ، فستكون هناك مشاكل. OBJ2: = tc2.create ؛ obj2.att1: = 0 ؛
intf1: = obj2 ؛ // صحيح. OBJ2: = intf1 ؛ OBJ2.ATT1: = 0 ؛ وهذا هو ، بعد التحويل من مرجع الكائن إلى مرجع المؤشر ، يتغير العنوان ، ولكن عندما يتم إعادة مرجع المؤشر من مرجع الكائن ثم يتم إرجاعه إلى مرجع الكائن (علة Delphi؟).
فخ 2. إدارة مدى الحياة
استنادًا إلى حسابي السليم (هنا هو برمجة الفطرة السليمة ، وليس Delphi Usage Sense السليم) ، أعتقد أن الواجهات لا تتطلب إدارة مدى الحياة ، لأن الواجهات لا يمكن أن تولد كائنات حقيقية على الإطلاق. لكن دلفي ضرب مرة أخرى حسنا السليم (هاه ، لماذا تقول "تجديد"؟) ، الواجهة لها مدى الحياة ، ويجب أن تنفذ الطرق الثلاثة التالية: وظيفة QueryInterface (const iid: tguid ؛ Out OBJ): hresult ؛ stdcall ؛ لذلك لا أعرف كيفية تنفيذ هذه الطرق الثلاث. J إذا كنت لا ترغب في تنفيذ هذه الأساليب الثلاث بنفسك ، فيمكنك استخدام TComponent. نظرًا لأن TComponent قد نفذت هذه الطرق الثلاث ، فقد يتم مورثها منها ، لذلك ليست هناك حاجة لتنفيذ هذه الطرق الثلاث. هل يمكنك استخدامه بثقة؟ الجواب لا. لأن Delphi سراً (لأنها كانت تتجاوز توقعاتي) عندما تقوم بتعيين متغير الواجهة على لا شيء. الدالة _intfclear (var dest: INTERST): VAR P: POINTER ؛ ) ._release ؛ وظيفة tcomponent._Release: integer بدأ إذا كان fvclcomoBject ، ثم لن يكون هناك شيء لم يفعل ذلك. ما الذي لا نعمل فيه ككائنات كوم ، لذلك لا توجد مشكلة؟ الجواب لا ، النظر في الموقف التالي: = tc2.create ؛ سيحدث خطأ في الوصول إلى العنوان غير القانوني. لماذا؟ كما ذكر أعلاه ، عندما يتم تعيين مرجع الواجهة على NIL ، سيتم استدعاء _intfclear ، وسيقوم _intfclear بالاتصال بـ _RELEASE للكائن. يقول بعض الناس أنه غير ضروري؟ OBJ2: = tc2.create ؛ tryintf1: = obj2 ؛ intf1.do ؛ لماذا؟ نظرًا لأن برنامج التحويل البرمجي Delphi ذكي ، فهو يعتقد أنك قد نسيت تعيين هذا العنوان إلى NIL ، لذلك ستقوم بإضافته تلقائيًا. كيف تحلها؟ الطريقة 1: قم بتعيين مرجع الواجهة إلى NIL أولاً ، ثم حرر الكائن. intf1: = nil ؛ المؤشر (intf1): = NIL ؛ أميل إلى استخدام الطريقة الثانية حتى لا تضطر إلى التفكير في من يجب إصداره أولاً. علاوة على ذلك ، في بعض أنماط التصميم ، يمكنك فقط الاحتفاظ بمراجع واجهة ، ولا تعرف متى سيتم إصدار الكائن المشار إليه. على سبيل المثال ، النظر في النمط المركب. TComposite = فئة (tcomponent ، i1) interlist private: txcontainer ؛ // فئة حاوية تخزن مرجع الواجهة لـ "Leaf". الإجراء العام إضافة (Iintf: i1) ؛ من الواضح أنه لا ، فهل سيتم إطلاق "الأوراق" بالتأكيد في وقت لاحق من كائن "كائن التوليف هذا"؟ أنا لا أعتقد ذلك. إذا تم تكليف هذه اللائحة ، فسيتم فقد الكثير من المرونة. لذلك نعتقد بالتأكيد أنه عندما يتم وضع مراجع الواجهة هذه لا شيء ، لن تكون هناك علاقة مع الكائن الأصلي ، وذلك لتجنب أخطاء الوصول إلى العناوين غير القانونية بعد إصدار الكائن. ما هي الحاوية التي يجب أن تفكر في استخدامها؟ صفيف؟ stlist؟ Tinterfacelist؟ بادئ ذي بدء ، أعتقد أنه يجب أن يكون tinterfacelist ، لأن ما نريد استيعابه هو الواجهة. ولكن عندما يحرر عليه ، فإنه يضع جميع الواجهات التي تستوعبها لا شيء ، وهذا بالضبط ما لا نريده. أو يمكننا تحويل مرجع الواجهة الذي يخزنه إلى مؤشر قبل مجانًا ثم ضبطه على لا شيء. لـ i: = 0 to interlist.count -1 dopointer (interlist.items [i]): = nil ؛ لسوء الحظ ، فإن خطأ التجميع "[خطأ] xxxx.pas (xx): لا يمكن تعيين الجانب الأيسر إلى". ثم نحاول الصفيف. interlist: لا ينبغي إصدار مجموعة من المصفوفات الديناميكية. يبدو أنه مفيد للغاية ، ولكن عندما يطلقه المترجم ، فإنه سيستمر في تعيين كل عنصر على لا شيء ، وكواجهة ، لا يزال هناك إمكانية لأخطاء الوصول غير القانونية للوصول إلى العنوان. يمكن القيام به بهذه الطريقة لـ i: = ARR (ARR) إلى DOPOINTER عالية (ARR): = NIL ؛ الوقت الذي تستخدمه ، وقد لا تتذكر القيام بذلك أم لا. وأخيرا ، استخدم stlist. ومع ذلك ، هناك مؤشر في stlist ، لذلك عند إضافة ، يجب أن تتابع على النحو التالي: XXX.Add (AINTF: I1) ابدأ interlist.add (المؤشر (AINTF)) ؛ تحتاج إلى معالجة خاصة عند إطلاقها. يبدو أنه مثالي ، ولكن لا يزال هناك فخ. interlist.add (tc2.create) ؛ نظرًا لأنه مؤشر نقي ، عند تحويله إلى واجهة ، لا يتم إجراء تحويل العنوان للكائن المشار إليه إلى مرجع الواجهة (لا يعرف كيفية القيام بذلك) ، لذلك عند استدعاء الطريقة التي تعلن عنها الواجهة ، إنه خطأ آخر في الوصول إلى عنوان غير قانوني. هذه هي الطريقة الوحيدة للكتابة: interlist.add (المؤشر (i1 (tc2.create))) ؛ على الرغم من أنها مزعجة بعض الشيء ، فإن هذا هو الحل الأفضل (كما أعرف). لأنه إذا نسيت أن النقل يخطئ في المكالمة الأولى ، فمن الأسهل العثور على أخطاء (مقارنة باستخدام Array). لذلك أقترح: 1. استخدم led لإدارة مراجع الواجهة. 2. عند الإضافة ، يجب عليك تحويل الكائن إلى واجهة ثم إلى مؤشر. 3. بالنسبة للمراجع الواجهة التي لم تتم إدارتها باستخدام led. للإشارة إلى الواجهات ، يجب أن تعهد يدويًا على الطريقة التالية: المؤشر (Intfref): = NIL ؛ لذلك ، يمكن أن تنقذ الوراثة من ذلك عن مشكلة تنفيذ هذه الأساليب الثلاث. ولكن يتم تنفيذ _Release على هذا النحو: Function tinterfacedObject._Release: integer. الكائن. هذه هي الطريقة التي يتم بها تنفيذها ، فهي تجلب مشكلة أكبر من الوراثة من TComponent. ما لم يتم استخدامه خلاف ذلك ، لا ينصح. تقتصر جميع المناقشات على واجهات أعلاه على الواجهات على مستوى اللغة المستخدمة بشكل عام ، ولا توجد مناقشة على واجهات COM+. إن تنفيذ هذه الواجهات الغريبة في Delphi له علاقة كبيرة بالتطور من واجهة COM+ ، وهو نتيجة للتسوية التي تم إجراؤها بسبب العبء التاريخي الثقيل.