2012-05-09 15 views
29

follwing स्थिति से शुरू:कक्षा या इंटरफ़ेस के लिए IDISposable घोषित करें?

public interface ISample 
{ 
} 

public class SampleA : ISample 
{ 
    // has some (unmanaged) resources that needs to be disposed 
} 

public class SampleB : ISample 
{ 
    // has no resources that needs to be disposed 
} 

वर्ग SampleA संसाधनों को रिहा करने के लिए इंटरफ़ेस IDisposable लागू करना चाहिए। आप दो तरह से इस का समाधान कर सकता:

public class SampleA : ISample, IDisposable 
{ 
    // has some (unmanaged) resources that needs to be disposed 
} 

2. इंटरफ़ेस ISample और बल कक्षाएं इसे लागू करने के व्युत्पन्न करने के लिए इसे जोड़ें:

1. वर्ग SampleA के लिए आवश्यक इंटरफेस जोड़ें:

public interface ISample : IDisposable 
{ 
} 

आप इसे इंटरफ़ेस में डाल दिया तो आप IDisposable लागू करने के लिए किसी भी कार्यान्वयन के लिए मजबूर भले ही वे निपटान के लिए कुछ भी नहीं है। दूसरी तरफ, यह देखना बहुत स्पष्ट है कि इंटरफ़ेस के ठोस कार्यान्वयन के लिए ब्लॉक का उपयोग/उपयोग करने की आवश्यकता होती है और आपको सफाई के लिए आईडीस्पोज़ेबल के रूप में डालने की आवश्यकता नहीं होती है। दोनों तरीकों से कुछ और पेशेवर/विपक्ष हो सकते हैं ... आप दूसरे तरीके से पसंदीदा तरीके का उपयोग करने का सुझाव क्यों देंगे?

+4

इंटरफ़ेस के खिलाफ लिखे गए कोड (संभावित रूप से पहले से निर्मित किए गए उदाहरण को सौंपने) कितनी संभावना है कि उस उदाहरण के * अंतिम * उपयोगी जीवनकाल के लिए ज़िम्मेदार है? –

+1

@Damien_The_Unbeliever: ठीक है, मानते हैं कि ISample कारखाने विधि के परिणाम या निर्भरता इंजेक्शन के माध्यम से आता है। – Beachwalker

+2

यह बात है - अगर यह * कारखाने से आ रही है, तो संभवतः आपका कोड * निपटान के लिए ज़िम्मेदार है - इसलिए मैं इसे इंटरफ़ेस पर रखूंगा। लेकिन अगर इसे इंजेक्शन दिया जा रहा है तो मुझे लगता है कि इंजेक्टर जीवन भर के लिए ज़िम्मेदार भी है, इसलिए यह * इंटरफेस पर फिट नहीं होगा - मुझे नहीं लगता कि एक आकार का फिट है-सवाल का सभी जवाब। –

उत्तर

6

"आसानी से उपयोग"से अधिक "आसान-कार्यान्वयन आप अपने सभी इंटरफेस के using(){} प्रतिमान लागू करते हैं यह ISampleIDisposable से निकाले जाते हैं है क्योंकि अंगूठे का नियम जब इंटरफेस डिजाइन करने के पक्ष में है सबसे अच्छा है "

+0

आप एक foreach में एक उपयोग कथन का उपयोग कैसे करेंगे? –

+1

"... और आप केवल इंटरफ़ेस देखते हैं ..." यह कुंजी है। बहुत अच्छे, ओओपी समाधान केवल इंटरफ़ेस का उपयोग करेंगे, इसलिए मुझे लगता है कि इंटरफ़ेस के लिए IDISposable लागू करना समझ में आता है। इस तरह उपभोग करने वाला कोड सभी उप-वर्गों को उसी तरह से इलाज कर सकता है। –

6

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

लगता है जैसे आपके पास बाद का मामला है।

+0

लेकिन निपटान() का आह्वान कैसे सुनिश्चित करें यदि आपके lib के उपयोगकर्ता को यह पता नहीं है और निपटान के लिए आवश्यक कार्यान्वयन हो सकता है? – Beachwalker

+0

@Stegi - आसान। बस 'आईडीस्पोजेबल' जांचें और यदि ऐसा है तो 'निपटान करें' पर कॉल करें। – Jamiec

+0

हाँ मुझे पता है, लेकिन यह कोड को बकवास करेगा ... क्योंकि हर दूसरे कार्यान्वयन (जैसे IList, I ...) IDisposable हो सकता है। मुझे लगता है जैसे IDISposable एक सामान्य वस्तु बेस क्लास कार्यान्वयन होना चाहिए (भले ही खाली हो)। – Beachwalker

0

व्यक्तिगत रूप से मैं 1 का चयन करूंगा, जब तक आप दो के लिए ठोस उदाहरण नहीं बनाते। दो का एक अच्छा उदाहरण IList है।

एक IList का अर्थ है कि आपको अपने संग्रह के लिए एक सूचकांक को लागू करने की आवश्यकता है। हालांकि, IList वास्तव में इसका भी अर्थ है कि आप IEnumerable हैं, और आपके पास अपनी कक्षा के लिए GetEnumerator() होना चाहिए।

अपने मामले में आप hesistant कि कक्षाओं कि ISample लागू IDisposable लागू करने के लिए, नहीं तो हर वर्ग है कि अपने इंटरफेस को लागू करता है IDisposable तो लागू करने के लिए उन्हें करने के लिए मजबूर नहीं है की आवश्यकता होगी रहे हैं।

विशेष रूप से, विशेष बलों प्रोग्रामर में IDispoable कुछ हद बदसूरत कोड लिखने के लिए अपने वर्ग का उपयोग कर IDispoable पर ध्यान केंद्रित। उदाहरण के लिए,

foreach(item in IEnumerable<ISample> items) 
{ 
    try 
    { 
     // Do stuff with item 
    } 
    finally 
    { 
     IDisposable amIDisposable = item as IDisposable; 
     if(amIDisposable != null) 
      amIDisposable.Dispose(); 
    } 
} 

न केवल कोड भयानक है, यह सुनिश्चित एक अंत में उस सूची के हर यात्रा के लिए आइटम के निपटान के लिए ब्लॉक, भले ही Dispose() बस में देता है में एक महत्वपूर्ण प्रदर्शन जुर्माना किया जाएगा कार्यान्वयन।

यहां पढ़ने में आसान एक टिप्पणी का उत्तर देने के लिए कोड को चिपकाया गया, पढ़ने में आसान।

+0

के प्रदर्शन प्रभाव के साथ ठीक हैं, लेकिन क्या होगा यदि कारखानों (आईओसी-कंटेनर का अयस्क सरल उपयोग) है जो कार्यान्वयन ऑब्जेक्ट्स को डिस्पोजेक्ट करने की आवश्यकता के साथ या बिना प्रदान करता है? आपको इसे कास्टिंग करके स्पष्ट रूप से कॉल करके निपटान() को कॉल करना होगा। इस तरह आपके कोड को (संभव) कार्यान्वयन के बारे में ज्ञान चाहिए ... कुछ प्रकार की युग्मन? अन्यथा आप केवल एक प्रयोग ब्लॉक का उपयोग कर सकते हैं जो बहुत आसान है। ब्लॉक का उपयोग कर – Beachwalker

+0

@Stegi बदसूरत नहीं लग सकता है, लेकिन यह अभी भी प्रदर्शन जुर्माना होगा। इसके अलावा मैं नहीं देखता कि आप उदाहरण के लिए एक foreach के अंदर उपयोग ब्लॉक का उपयोग कैसे करेंगे।माइक्रोसॉफ्ट का कोड उन उदाहरणों के साथ लाइट किया गया है जहां यह ऐसा करता है, IDISposable amidisposable = वस्तु IDISposable के रूप में; अगर (amidisposable! = शून्य) amidisposable। उद्देश्य(); चूंकि यह अपवाद नहीं फेंकता है अगर यह इसे एक आईडीस्पोजेबल में नहीं डाला जा सकता है, तो प्रदर्शन जुर्माना मौजूद नहीं है। –

+0

भले ही कुछ विशेष प्रकार से लागू या प्राप्त करने वाले वर्गों में से केवल 1% 'IDISposable.Dispose 'में कुछ भी करेंगे, यदि किसी भी चर या फ़ील्ड पर उस प्रकार के रूप में घोषित किए गए फ़ील्ड पर' IDISposable.Dispose 'को कॉल करने के लिए आवश्यक हो, एक कार्यान्वयन या व्युत्पन्न प्रकार में से एक के विपरीत) यह एक अच्छा संकेत है कि इस प्रकार के प्रकार को 'आईडीस्पोजेबल' का उत्तराधिकारी या कार्यान्वित करना चाहिए। रन-टाइम पर परीक्षण करना कि क्या ऑब्जेक्ट इंस्टेंस 'आईडीस्पोजेबल' लागू करता है और 'IDISposable' को कॉल करता है। अगर ऐसा होता है (जैसा गैर-जेनेरिक 'आईनेमरेबल' के साथ आवश्यक था) एक प्रमुख कोड गंध है। – supercat

2

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

+0

यह बस सच नहीं है। यदि आपकी कक्षा में ऐसा क्षेत्र है जो IDISposable लागू करता है कि कक्षा को क्षेत्र के निपटान को कॉल करने के लिए भी IDISposable होना चाहिए। यदि आप आवश्यकता नहीं होने पर IDISposable जोड़ते हैं तो आप अपने आधे वर्गों को आईडीस्पोजेबल के साथ समाप्त कर देते हैं। –

3

एक इंटरफेस IFoo शायद IDisposable को लागू करना चाहिए अगर यह संभावना है कि कम से कम कुछ कुछ कार्यान्वयन IDisposable को लागू करेगा, और कम से कम कुछ अवसरों पर एक उदाहरण के लिए अंतिम जीवित संदर्भ में एक चर या प्रकार IFoo के क्षेत्र में संग्रहीत किया जाएगा। यह लगभग निश्चित रूप IDisposable को लागू करना चाहिए, यदि कोई हो कार्यान्वयन IDisposable लागू हो सकता है और उदाहरणों कारखाने इंटरफ़ेस के माध्यम से बनाया जाएगा (IEnumerator<T> के उदाहरण, कई मामलों में कारखाना इंटरफ़ेस IEnumerable<T> के माध्यम से बनाई गई हैं, जिसके साथ मामला है)।

IEnumerable<T> और IEnumerator<T> की तुलना निर्देशक है। कुछ प्रकार के जो IEnumerable<T> लागू भी IDisposable लागू है, लेकिन कोड है जो इस तरह प्रकार के उदाहरण बनाता है कि वे क्या कर रहे हैं, पता है कि वे निपटान की जरूरत है पता चल जाएगा, और उन्हें अपने विशेष प्रकार के रूप में इस्तेमाल करते हैं। ऐसे मामलों प्रकार IEnumerable<T> के रूप में अन्य दिनचर्या के लिए पारित किया जा सकता है और उन अन्य दिनचर्या कोई सुराग नहीं है कि वस्तुओं अंततः निपटाने की जरूरत करने जा रहे हैं के लिए होता है, लेकिन उन अन्य दिनचर्या ज्यादातर मामलों में पिछले वाले वस्तुओं के लिए संदर्भ धारण करने के लिए नहीं होगा। इसके विपरीत, IEnumerator<T> के उदाहरण अक्सर कोड द्वारा बनाए जाते हैं, उपयोग किए जाते हैं, और आखिरकार त्याग दिए जाते हैं, जो उन उदाहरणों के अंतर्निहित प्रकारों के बारे में कुछ भी नहीं जानते हैं, जिन्हें वे IEnumerable<T> द्वारा लौटाए जाते हैं। IEnumerable<T>.GetEnumerator() के कुछ कार्यान्वयन IEnumerator<T> के रिटर्न कार्यान्वयन संसाधनों को रिसाव करेंगे यदि उनकी IDisposable.Dispose विधि को छोड़ने से पहले नहीं कहा जाता है, और अधिकांश कोड जो IEnumerable<T> के मानकों को स्वीकार करते हैं, उन्हें यह जानने का कोई तरीका नहीं होगा कि इस तरह के प्रकार पास किए जा सकते हैं या नहीं। हालांकि IEnumerable<T> के लिए EnumeratorTypeNeedsDisposal संपत्ति को शामिल करने के लिए यह संभव होगा कि IEnumerator<T> को वापस लेना होगा या GetEnumerator() पर कॉल करने वाले दिनचर्या की आवश्यकता है, यह देखने के लिए कि यह IDisposable लागू करता है या नहीं, यह तेज़ और आसान है बिना किसी शर्त के Dispose विधि को कॉल करने के लिए जो कुछ भी नहीं कर सकता है, यह निर्धारित करने के लिए कि Dispose आवश्यक है और इसे केवल तभी कॉल करें।

+0

'आईन्यूमेरेटर ' एक इंटरफ़ेस का एक अच्छा उदाहरण है जहां आजीवन प्रबंधन निश्चित रूप से इसके एपीआई का हिस्सा है। यह संभवतः चीजों को आसान रखने के लिए किया गया था। हालांकि, यह कल्पना की जा सकती है कि कोई एक विधि में 'GetEnumerator() 'को कॉल करना चाहता है और कुछ बिंदु पर गणनाकर्ता को दूसरी विधि में पास करना होगा। उस अन्य विधि को आदर्श रूप से 'निपटान() 'तक पहुंच नहीं होगी। यदि ढांचे में 'इंटरफ़ेस IDISposableEnumerator था: IENumerator , IDISposable {} 'और' इंटरफ़ेस IENumerator : IENumerator {T Current {get; }} ', यह कुछ चीजें "क्लीनर" कर सकता है (लेकिन अधिक जटिल भी: - /)। – binki

+1

@ बिन्की: 'आईडीस्पोज़ेबल' को लागू करने के लिए चीजें क्लीनर 'आईन्यूमेरेटर' के लिए क्या होता। कोड जो 'आईनेमरेबल' प्राप्त करता है, उसे यह नहीं पता होना चाहिए कि 'आईनेम्युलेटर' पर वापस 'निपटान' को कॉल करना आवश्यक हो सकता है। यदि यह कुछ समय बाद खपत के लिए किसी अन्य विधि में लौटाए गए गणक को पास करता है, तो उस बाद की विधि में 'निपटान' को कॉल करने की ज़िम्मेदारी हो सकती है। चूंकि 'आईडीस्पोजेबल' को लागू करने के लिए जाने वाली किसी चीज़ पर कुछ भी नहीं किया गया है, यह जांचने से तेज़ है कि कोई ऑब्जेक्ट 'आईडीस्पोजेबल' लागू करता है या नहीं ... – supercat

+1

... ऑब्जेक्ट्स को अपने दायित्वों को पूरा करने का सबसे आसान तरीका कॉल करना होगा उन्हें छोड़ने से पहले गणितकर्ताओं पर उनका निपटान करें, भले ही उन गणककों की परवाह न हो। – supercat

16

आप ए

इसके अलावा करने के लिए इसे जोड़ने चाहिए अगर आप इंटरफ़ेस आप ग्राहकों कि इतने में कोई दिलचस्पी नहीं कर रहे हैं करने के तरीकों को दे रहे हैं करने के लिए IDisposable जोड़ने SOLID की Inteface Segregation Principle के बाद, एक अंतरफलक वजह से कभी डिस्पोजेबल है डिस्पोजेबिलिटी इंटरफ़ेस के ठोस कार्यान्वयन से संबंधित कुछ है, कभी इंटरफ़ेस के साथ नहीं।

किसी भी इंटरफ़ेस संभवतः के साथ या तत्वों निपटारा करने के लिए जरूरत है कि बिना लागू किया जा सकता।

1

मुझे लगता है कि करने के लिए है कि एक इंटरफेस पर IDisposable डाल कुछ समस्याएं पैदा कर सकता शुरू कर रहा हूँ। इसका तात्पर्य है कि उस इंटरफ़ेस को लागू करने वाली सभी वस्तुओं का जीवनकाल सुरक्षित रूप से सिंक्रनाइज़ रूप से समाप्त हो सकता है। यानी, वह किसी को भी इस तरह कोड लिखने की अनुमति देता है और IDisposable समर्थन करने के लिए सभी कार्यान्वयन की आवश्यकता है:

using (ISample myInstance = GetISampleInstance()) 
{ 
    myInstance.DoSomething(); 
} 

केवल कोड है जो ठोस प्रकार तक पहुँच रहा है वस्तु के जीवनकाल को नियंत्रित करने के सही तरीके से पता कर सकते हैं।उदाहरण के लिए, किसी प्रकार को पहले स्थान पर निपटान की आवश्यकता नहीं हो सकती है, यह IDisposable का समर्थन कर सकता है, या इसे उपयोग करने के बाद awaiting कुछ एसिंक्रोनस क्लीनअप प्रक्रिया की आवश्यकता हो सकती है (उदा।, something like option 2 here)।

एक इंटरफ़ेस लेखक कक्षाओं को लागू करने के सभी संभावित भविष्य के जीवनकाल/स्कोप प्रबंधन आवश्यकताओं की भविष्यवाणी नहीं कर सकता है। किसी इंटरफ़ेस का उद्देश्य किसी ऑब्जेक्ट को कुछ API को बेनकाब करने की अनुमति देना है ताकि इसे कुछ उपभोक्ता के लिए उपयोगी बनाया जा सके। कुछ इंटरफेस जीवनकाल प्रबंधन से संबंधित हो सकते हैं (जैसे कि IDisposable स्वयं), लेकिन उन्हें जीवन भर प्रबंधन से संबंधित इंटरफेस के साथ मिलाकर इंटरफ़ेस को हार्ड या असंभव के कार्यान्वयन को लिखना हो सकता है। यदि आपके इंटरफ़ेस के बहुत कम कार्यान्वयन हैं और आपका कोड तैयार करते हैं ताकि इंटरफ़ेस का उपभोक्ता और जीवनकाल/स्कोप-मैनेजर एक ही विधि में हो, तो यह भेद पहले स्पष्ट नहीं है। लेकिन अगर आप अपने ऑब्जेक्ट को पास करना शुरू करते हैं, तो यह स्पष्ट होगा।

void ConsumeSample(ISample sample) 
{ 
    // INCORRECT CODE! 
    // It is a developer mistake to write “using” in consumer code. 
    // “using” should only be used by the code that is managing the lifetime. 
    using (sample) 
    { 
     sample.DoSomething(); 
    } 

    // CORRECT CODE 
    sample.DoSomething(); 
} 

async Task ManageObjectLifetimesAsync() 
{ 
    SampleB sampleB = new SampleB(); 
    using (SampleA sampleA = new SampleA()) 
    { 
     DoSomething(sampleA); 
     DoSomething(sampleB); 
     DoSomething(sampleA); 
    } 

    DoSomething(sampleB); 

    // In the future you may have an implementation of ISample 
    // which requires a completely different type of lifetime 
    // management than IDisposable: 
    SampleC = new SampleC(); 
    DoSomething(sampleC); 
    sampleC.Complete(); 
    await sampleC.Completion; 
} 

class SampleC : ISample 
{ 
    public void Complete(); 
    public Task Completion { get; } 
} 

उपरोक्त कोड नमूने में, मैंने आपके द्वारा प्रदान किए गए दो में जोड़े गए तीन प्रकार के आजीवन प्रबंधन परिदृश्यों का प्रदर्शन किया।

  1. SampleA तुल्यकालिक using() {} समर्थन के साथ IDisposable है।
  2. SampleB शुद्ध कचरा संग्रह का उपयोग करता है (यह किसी भी संसाधन का उपभोग नहीं करता है)।
  3. SampleC उन संसाधनों का उपयोग करता है जो इसे सिंक्रनाइज़ तरीके से निपटाए जाने से रोकते हैं और इसके जीवनकाल के अंत में await की आवश्यकता होती है (ताकि यह जीवन भर प्रबंधन प्रबंधन कोड को सूचित कर सके कि यह संसाधनों का उपभोग कर रहा है और किसी भी असीमित रूप से सामने आए अपवादों को बुलबुला करता है)।

रखने जीवन प्रबंधन द्वारा अपने अन्य इंटरफेस से अलग है, तो आप (Dispose() लिए उदाहरण के लिए, आकस्मिक कॉल) डेवलपर गलतियों को रोकने के लिए और अधिक सफाई से भविष्य अप्रत्याशित जीवन/गुंजाइश प्रबंधन पैटर्न का समर्थन कर सकते हैं।

+0

जिन प्रकारों को क्लीनअप की आवश्यकता नहीं है, वे 'शून्य आईडीस्पोजेबल' के माध्यम से आसानी से और सही ढंग से 'आईडीस्पोजेबल' को कार्यान्वित कर सकते हैं। (उदा।() {}; '। यदि फ़ैक्टरी फ़ंक्शन से लौटाई गई कुछ ऑब्जेक्ट्स को क्लीनअप की आवश्यकता होगी, तो यह आसान और आसान है कि यह एक प्रकार वापस लौटाए जो 'आईडीस्पोजेबल' लागू करता है और यह आवश्यक है कि क्लाइंट फ़ैक्टरी फ़ंक्शन में प्रत्येक कॉल को लौटाए गए ऑब्जेक्ट के 'डिस्प्ले' से कॉल करें आवश्यकता है कि ग्राहक निर्धारित करें कि कौन सी वस्तुओं को क्लीनअप की आवश्यकता है। – supercat

संबंधित मुद्दे