2009-04-16 11 views
12

मैं निम्नलिखित इंटरफेस और एक वर्ग है कि उन्हें लागू करता है - '! बुरा'डेल्फी में, आप कैसे जांच सकते हैं कि एक IInterface संदर्भ एक व्युत्पन्न लेकिन स्पष्ट रूप से समर्थित इंटरफ़ेस लागू नहीं करता है?

IBase = Interface ['{82F1F81A-A408-448B-A194-DCED9A7E4FF7}'] 
End; 

IDerived = Interface(IBase) ['{A0313EBE-C50D-4857-B324-8C0670C8252A}'] 
End; 

TImplementation = Class(TInterfacedObject, IDerived) 
End; 

निम्नलिखित कोड प्रिंट -

Procedure Test; 
Var 
    A : IDerived; 
Begin 
    A := TImplementation.Create As IDerived; 
    If Supports (A, IBase) Then 
     WriteLn ('Good!') 
    Else 
     WriteLn ('Bad!'); 
End; 

यह थोड़ा कष्टप्रद लेकिन समझ में आता है। समर्थन आईबीएएस में नहीं डाला जा सकता है क्योंकि आईबीएएस जीआईआईडी की सूची में नहीं है जो टीआईपीमेंटेशन का समर्थन करता है।

TImplementation = Class(TInterfacedObject, IDerived, IBase) 

अभी तक कर कि मैं पहले से ही पता कि एक को लागू करता घोषित क्योंकि एक एक IDerived है और एक IDerived एक घोषित है बिना - यह करने के लिए घोषणा बदलते द्वारा निर्धारित किया जा सकता है। तो अगर मैं चेक बाहर छोड़ मैं एक डाल सकता है और सब कुछ ठीक हो जाएगा -

Procedure Test; 
Var 
    A : IDerived; 
    B : IBase; 
Begin 
    A := TImplementation.Create As IDerived; 
    B := IBase(A); 
    //Can now successfully call any of B's methods 
End; 

लेकिन हम एक समस्या का सामना करना है जब हम एक सामान्य कंटेनर में IBases डालना आरंभ - उदाहरण के लिए TInterfaceList। यह केवल IInterfaces पकड़ सकता है तो हमें कुछ कास्टिंग करना है।

Procedure Test2; 
Var 
    A : IDerived; 
    B : IBase; 
    List : TInterfaceList; 
Begin 
    A := TImplementation.Create As IDerived; 
    B := IBase(A); 

    List := TInterfaceList.Create; 
    List.Add(IInterface(B)); 
    Assert (Supports (List[0], IBase)); //This assertion fails 
    IBase(List[0]).DoWhatever; //Assuming I declared DoWhatever in IBase, this works fine, but it is not type-safe 

    List.Free; 
End; 

मैं बहुत ज्यादा किसी भी बेमेल प्रकार को पकड़ने के लिए दावे के कुछ प्रकार करना चाहते हैं - बात की इस तरह Is ऑपरेटर का उपयोग की वस्तुओं के साथ किया जा सकता है, लेकिन वह इंटरफेस के लिए काम नहीं करता। विभिन्न कारणों से, मैं समर्थित इंटरफ़ेस की सूची में स्पष्ट रूप से आईबीएएस नहीं जोड़ना चाहता हूं। क्या कोई तरीका है कि मैं टीआईमिमेंटेशन और इस तरह से दावा लिख ​​सकता हूं कि यह सही iff हार्ड-कास्टिंग आईबीएएस (सूची [0]) का मूल्यांकन करेगा एक सुरक्षित बात है?

संपादित करें:

के रूप में यह जवाब, मैं दो प्रमुख कारण जोड़ रहा मैं इंटरफ़ेस TImplementation लागू करता है की सूची में घोषित जोड़ने के लिए नहीं करना चाहते हैं में से एक में आया।

सबसे पहले, यह वास्तव में समस्या को हल नहीं करता है। , तो Test2, अभिव्यक्ति में:

Supports (List[0], IBase) 

रिटर्न सच, इसका मतलब यह नहीं है कि यह एक कठिन डाली प्रदर्शन करने के लिए सुरक्षित है। अनुरोध इंटरफ़ेस को संतुष्ट करने के लिए QueryInterface एक अलग पॉइंटर लौटा सकता है। उदाहरण के लिए, यदि TImplementation स्पष्ट रूप से लागू करता है दोनों घोषित और IDerived (और IInterface), तो दावे को सफलतापूर्वक पारित करेंगे:

Assert (Supports (List[0], IBase)); //Passes, List[0] does implement IBase 

लेकिन कल्पना किसी ने गलती से किसी IInterface

List.Add(Item As IInterface); 
के रूप में सूची में कोई आइटम जोड़ता है कि

दावा अभी भी गुजरता है - आइटम अभी भी आईबीएएस लागू करता है, लेकिन सूची में जोड़ा गया संदर्भ केवल एक IInterface है - इसे आईबीएएस में हार्ड-कास्टिंग करने से कुछ भी समझदार नहीं होगा, इसलिए यह जांचने में पर्याप्त नहीं है कि निम्नलिखित कठोर -कास्ट सुरक्षित है। एक ही तरीका है कि काम करने के लिए गारंटी है एक के रूप में उपयोग करने के लिए डाली है या समर्थन करता होगा:

(List[0] As IBase).DoWhatever; 

लेकिन यह एक निराशा प्रदर्शन लागत के रूप में यह कोड को सूची में आइटम जोड़ने की जिम्मेदारी करने का इरादा है है, सुनिश्चित करें कि वे आईबीएएस के प्रकार हैं - हमें यह मानने में सक्षम होना चाहिए (इसलिए यह धारणा झूठ बोलने का दावा है)।यदि कोई भी कुछ प्रकार बदलता है तो बाद में गलतियों को पकड़ने के अलावा, दावा भी आवश्यक नहीं है। मूल समस्या यह समस्या आती है, यह भी काफी महत्वपूर्ण है, इसलिए एक प्रदर्शन लागत जो कम प्राप्त करती है (यह अभी भी रन-टाइम पर केवल विचलित प्रकारों को पकड़ती है, लेकिन बिना किसी तेज रिलीज बिल्ड को संकलित करने की संभावना के बिना) कुछ ऐसा है जो मैं बचाना चाहता हूं ।

दूसरा कारण यह है कि मैं समानता के संदर्भों की तुलना करने में सक्षम होना चाहता हूं, लेकिन ऐसा नहीं किया जा सकता है यदि एक ही कार्यान्वयन वस्तु विभिन्न वीएमटी ऑफ़सेट के साथ विभिन्न संदर्भों द्वारा आयोजित की जाती है।

संपादित करें 2: एक उदाहरण के साथ उपरोक्त संपादन का विस्तार किया।

संपादित करें 3: नोट: प्रश्न यह है कि मैं दावा कैसे बना सकता हूं ताकि कठोर-कास्ट सुरक्षित हो, अगर कथन पास हो जाए, तो हार्ड-कास्ट से कैसे बचें। हार्ड-कास्ट चरण को अलग-अलग करने के तरीके हैं, या इसे पूरी तरह से टालने के लिए, लेकिन यदि रनटाइम प्रदर्शन लागत है, तो मैं उनका उपयोग नहीं कर सकता। मैं दावे के भीतर जांच की सभी लागत चाहता हूं ताकि इसे बाद में संकलित किया जा सके।

यह कहकर कि, अगर कोई समस्या प्रदर्शन से पूरी तरह से समस्या से बच सकता है और कोई प्रकार की जांच खतरे नहीं होगी!

+0

मेरे परीक्षणों में GetInterfaceTable() में वीएमटी ऑफ़सेट बराबर था, भले ही दो अलग-अलग GUID लागू किए गए थे - यह समझ में आता है कि एक इंटरफ़ेस दूसरे से "विरासत" प्राप्त करता है, तो आधार केवल उसी तालिका के पहले भाग को इंगित करता है । – mghie

+0

क्या वह Win32 कोड में था? मैंने पाया कि प्रत्येक इंटरफ़ेस जिसे मैंने स्पष्ट रूप से TImplementation में जोड़ा है, क्लास इंस्टेंस आकार को 4 बाइट्स तक बढ़ाया - एक अतिरिक्त वीएमटी पॉइंटर। विरासत इंटरफेस से भी, प्रत्येक GUID का समर्थन करने के लिए पारित किया गया, एक अद्वितीय पता वापस कर देगा। – David

+0

कारण मैं पूछता हूं कि मुझे लगता है कि .NET डेल्फी अलग-अलग व्यवहार कर सकता है - मैं एक Win32 टैग – David

उत्तर

12

एक चीज जो आप कर सकते हैं स्टॉप टाइप-कास्टिंग इंटरफेस रोकें। आपको IDerived से IBase पर जाने की आवश्यकता नहीं है, और आपको IBase से IUnknown पर जाने की आवश्यकता नहीं है।IDerivedका कोई भी संदर्भ पहले से हीIBase है, इसलिए आप टाइपिंग के बिना भी IBase विधियों को कॉल कर सकते हैं। यदि आप कम टाइप-कास्टिंग करते हैं, तो आप कंपाइलर को आपके लिए और अधिक काम करने देते हैं और उन चीज़ों को पकड़ते हैं जो ध्वनि नहीं हैं।

आपका अनुमानित लक्ष्य यह जांचने में सक्षम होना है कि आप अपनी सूची से बाहर निकलने वाली चीज़ वास्तव में IBase संदर्भ है। एक लागू इंटरफ़ेस के रूप में IBase जोड़ना आपको उस लक्ष्य को आसानी से प्राप्त करने की अनुमति देगा। उस प्रकाश में, ऐसा करने के लिए आपके "दो प्रमुख कारण" में कोई पानी नहीं है।

  1. "मैं समानता के संदर्भों की तुलना करने में सक्षम होना चाहता हूं": कोई समस्या नहीं। COM की आवश्यकता है कि यदि आप एक ही ऑब्जेक्ट पर एक ही GUID के साथ QueryInterface दो बार कॉल करते हैं, तो आपको एक ही इंटरफ़ेस पॉइंटर दोनों बार मिलता है। यदि आपके पास दो मनमानी इंटरफ़ेस संदर्भ हैं, और आप as- दोनों को IBase पर रखें, तो परिणाम एक ही पॉइंटर मान होंगे यदि केवल वही ऑब्जेक्ट द्वारा समर्थित किया गया हो।

    के बाद से आप अपनी सूची में चाहते हैं कि केवल IBase मान होते हैं, और आप की जरूरत नहीं है डेल्फी कहाँ 2009 एक सामान्य TInterfaceList<IBase> मददगार होगा लगता है, आप अपने आप को अनुशासित कर सकते हैं हमेशा स्पष्ट रूप से कभी नहीं मान, सूची में IBase मान जोड़ने के लिए किसी भी वंशज प्रकार। जब भी आप इस तरह की सूची, उपयोग कोड में कोई आइटम जोड़ने: इस तरह

    List.Add(Item as IBase); 
    

    , सूची में किसी भी डुप्लिकेट का पता लगाने के लिए आसान कर रहे हैं, और अपने "हार्ड डाले" काम करने के लिए आश्वासन दिया जाता है।

  2. "यह वास्तव में समस्या को हल नहीं करता है": लेकिन यह उपर्युक्त नियम देता है।

    Assert(Supports(List[i], IBase)); 
    

    जब ऑब्जेक्ट स्पष्ट रूप से अपने सभी इंटरफेस लागू करता है, तो आप इस तरह की चीजों की जांच कर सकते हैं। और यदि आपने ऊपर वर्णित सूची में आइटम जोड़े हैं, तो यह दावा अक्षम करने के लिए सुरक्षित है। सम्मिलन को सक्षम करने से आपको पता चलता है कि किसी ने आपके प्रोग्राम में कहीं और कोड को गलत तरीके से सूची में जोड़ने के लिए कोड बदल दिया है। आपके यूनिट परीक्षणों को चलाने से अक्सर आपको पेश होने के तुरंत बाद समस्या का पता चल जाएगा।

मन में ऊपर अंक के साथ

, आप देख सकते हैं कि कुछ भी है कि सूची को जोड़ा गया है इस कोड के साथ सही ढंग से जोड़ा गया है:

var 
    AssertionItem: IBase; 

Assert(Supports(List[i], IBase, AssertionItem) 
     and (AssertionItem = List[i])); 
// I don't recall whether the compiler accepts comparing an IBase 
// value (AssertionItem) to an IUnknown value (List[i]). If the 
// compiler complains, then simply change the declaration to 
// IUnknown instead; the Supports function won't notice. 

तो दावे विफल रहता है, तो या तो आप में कुछ जोड़ा वह सूची जो IBase का समर्थन नहीं करती है, या किसी ऑब्जेक्ट के लिए जो विशिष्ट इंटरफ़ेस संदर्भ जोड़ा गया है, वह IBase संदर्भ के रूप में कार्य नहीं कर सकता है। यदि दावा पास हो जाता है, तो आप जानते हैं कि List[i] आपको एक वैध IBase मान देगा।

ध्यान दें कि सूची में जोड़ा गया मूल्य की आवश्यकता नहीं है IBase मूल्य स्पष्ट रूप से। ऊपर अपने प्रकार घोषणाओं को देखते हुए, यह सुरक्षित है:

var 
    A: IDerived; 
begin 
    A := TImplementation.Create; 
    List.Add(A); 
end; 

कि सुरक्षित है क्योंकि इंटरफेस TImplementation द्वारा कार्यान्वित एक विरासत पेड़ है कि एक साधारण सूची में घिनौना रूप में। ऐसी कोई शाखा नहीं है जहां दो इंटरफेस एक-दूसरे से प्राप्त न हों लेकिन एक आम पूर्वज हो।यदि IBase के निर्णायक थे, और TImplementation ने उन्हें लागू किया, तो उपर्युक्त कोड मान्य नहीं होगा क्योंकि IBaseA में आयोजित संदर्भ उस वस्तु के संदर्भ में "कैननिकल" IBase संदर्भ नहीं होगा। दावा उस समस्या का पता लगाएगा, और आपको इसके बजाय List.Add(A as IBase) के साथ जोड़ना होगा।

जब आप दावे को अक्षम करते हैं, तो सूची में जोड़ने के दौरान, सूची में जोड़ने के दौरान सही प्रकार प्राप्त करने की लागत केवल भुगतान की जाती है, सूची से पढ़ने के दौरान नहीं। मैंने परिवर्तनीय AssertionItem को प्रक्रिया में कहीं और उस चर का उपयोग करने से हतोत्साहित करने के लिए नाम दिया है; यह केवल दावा का समर्थन करने के लिए है, और दावाों को अक्षम करने के बाद उसके पास वैध मान नहीं होगा।

+0

एचएम, काश मैं इस जवाब को एक से अधिक बार बढ़ा सकता हूं। :-) –

+0

जेनरिक के बिना भी आप अपना खुद का टीबीएसलिस्ट (टीइंटरफेसलिस्ट के समान, आईबीएएस संदर्भों को पकड़ने के लिए) लिख सकते हैं। –

+0

आप सही हैं कि एएस-कास्टिंग द्वारा मैं अपनी सभी समस्याओं को हल कर सकता हूं, लेकिन प्रदर्शन लागत के साथ। यहां तक ​​कि अगर मैं स्पष्ट रूप से सभी इंटरफेस को टीआईमिमेंटेशन में जोड़ता हूं, तो यह सुनिश्चित नहीं करता है कि हार्ड-कास्ट सुरक्षित है, इसे एक कलाकार के रूप में जाना होगा। मैंने उस पर विस्तार करने के लिए अपना प्रश्न संपादित कर लिया है। – David

0

टेस्ट 2 में;

आप घोषित रूप IDerived फिर से लिखें shound't घोषित (ए) द्वारा लेकिन साथ:

Supports(A, IBase, B); 

और सूची में जोड़ने से सिर्फ हो सकता है:

List.Add(B); 
6

आप अपने परीक्षा में और के रूप में सही कर रहे हैं जहां तक ​​मैं कह सकता हूं कि आपके सामने आने वाली समस्या का कोई प्रत्यक्ष समाधान नहीं है। कारण इंटरफेस के बीच विरासत की प्रकृति में निहित हैं, जिसमें कक्षाओं के बीच विरासत का केवल एक अस्पष्ट समानता है। एक विरासत इंटरफेस एक नया नया इंटरफ़ेस है, जिसमें कुछ तरीकों से इंटरफ़ेस के साथ समानता है, लेकिन कोई प्रत्यक्ष कनेक्शन नहीं है। तो बेस क्लास इंटरफ़ेस को लागू न करने का चयन करके, आप एक विशिष्ट धारणा बना रहे हैं कि संकलित कार्यक्रम का पालन किया जाएगा: TImplementation IBase को लागू नहीं करता है। मुझे लगता है कि "इंटरफ़ेस विरासत" कुछ गलत नामक है, इंटरफ़ेस एक्सटेंशन अधिक समझ में आता है! एक सामान्य अभ्यास बेस इंटरफेस को लागू करने वाली बेस क्लास, और विस्तारित इंटरफेस को लागू करने वाले व्युत्पन्न वर्गों की तुलना में है, लेकिन यदि आप एक अलग वर्ग चाहते हैं जो लागू करता है तो बस उन इंटरफेस को सूचीबद्ध करें। इसका उपयोग करने से बचने के लिए एक विशिष्ट कारण है:

TImplementation = Class(TInterfacedObject, IDerived, IBase) 

या बस आपको यह पसंद नहीं है?

इसके अलावा टिप्पणी

तुम कभी, यहाँ तक कि कठिन प्रकार एक अंतरफलक डाली चाहिए। जब आप किसी इंटरफ़ेस पर "as" करते हैं तो यह ऑब्जेक्ट vtable पॉइंटर्स को सही तरीके से समायोजित करेगा ... यदि आप हार्ड कास्ट करते हैं (और कॉल करने के तरीके हैं) तो कोड आसानी से क्रैश हो सकता है। मेरी धारणा यह है कि आप वस्तुओं जैसे इंटरफेस का इलाज कर रहे हैं (विरासत का उपयोग करके और उसी तरह कास्ट करते हैं) जबकि उनके आंतरिक काम वास्तव में अलग हैं!

+0

+1 में कुछ प्रसिद्ध कंपाइलर जादू की अपेक्षा करता हूं। ध्यान दें कि यदि आप वास्तव में कार्यान्वित इंटरफेस की सूची में आईबीएएस का उल्लेख नहीं करना चाहते हैं, तो आप अभी भी आईडीरिव में आईबीएएस के कार्यों और प्रक्रियाओं को दोहरा सकते हैं जिन्हें आपको कॉल करने की आवश्यकता है - बिना आईडीरिव किए गए। – mghie

+0

आईबीएएस जोड़ने से बचने के कई कारण हैं, मैंने अब उन्हें मूल प्रश्न में जोड़ा है। मुझे डर था कि 'विरासत' इंटरफेस वास्तव में जुड़े नहीं हैं, जैसा कि आप कहते हैं। हालांकि मेरे पास कार्यान्वयन पर नियंत्रण है। क्या शायद एक साफ तरीका है जिसे मैं बाद में कार्यक्षमता पेश कर सकता हूं? – David