2012-04-23 11 views
9

मुझे कुछ कोड मिला है जिसमें बहुत अधिक नकल है। समस्या इस तथ्य से आती है कि मैं नेस्टेड IDisposable प्रकारों से निपट रहा हूं। आज मैं कुछ है कि तरह दिखता है:नेस्टेड उपयोग में शामिल एक रिफैक्टर कोड कैसे हो सकता है?

public void UpdateFromXml(Guid innerId, XDocument someXml) 
{ 
    using (var a = SomeFactory.GetA(_uri)) 
    using (var b = a.GetB(_id)) 
    using (var c = b.GetC(innerId)) 
    { 
     var cWrapper = new SomeWrapper(c); 
     cWrapper.Update(someXml); 
    } 
} 

public bool GetSomeValueById(Guid innerId) 
{ 
    using (var a = SomeFactory.GetA(_uri)) 
    using (var b = a.GetB(_id)) 
    using (var c = b.GetC(innerId)) 
    { 
     return c.GetSomeValue(); 
    } 
} 

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

एक तरह से मैं सोच रहा था की तर्ज पर कुछ करने के लिए होगा:

public void UpdateFromXml(Guid innerId, XDocument someXml) 
{ 
    ActOnC(innerId, c => 
    { 
     var cWrapper = new SomeWrapper(c); 
     cWrapper.Update(someXml); 
    }); 
} 

public bool GetSomeValueById(Guid innerId) 
{ 
    var result = null; 

    ActOnC(innerId, c => { result = c.GetSomeValue(); }); 

    return result; 
} 

private void ActOnC(Guid innerId, Action<TheCType> action) 
{ 
    using (var a = SomeFactory.GetA(_uri)) 
    using (var b = a.GetB(_id)) 
    using (var c = b.GetC(innerId)) 
    { 
     action(c); 
    }   
} 

यह काम करता है, यह सिर्फ एक तरह से (एक इंसान के रूप में) पार्स करने के लिए भद्दा है। क्या किसी के पास कोई अन्य सुझाव है कि कोई इस तरह के नेस्टेड using ब्लॉक के आसपास कोड डुप्लिकेशंस को कैसे कम कर सकता है? यदि वे IDisposable नहीं थे तो संभवतः b.GetC(innerId) के परिणामों को वापस करने के लिए एक विधि तैयार की जाएगी ... लेकिन यह मामला यहां नहीं है।

+3

+1 मैं कुछ भी अपने समाधान में भद्दा दिखाई नहीं देता। यह प्रक्रियात्मक से अधिक कार्यात्मक होने के रूप में अपरंपरागत है, लेकिन मैं इसे एक समर्थक के रूप में मानता हूं, न कि – mfeingold

+1

पर मुझे लगता है कि आपका कार्यान्वयन ठीक दिखता है लेकिन शायद आप नीचे दिए गए कुछ विकल्पों को प्राथमिकता देंगे। यदि आपको अपने आप को बहुत से डिस्पोजेबल चेन करना पड़ता है तो आप चीजों को फिर से डिजाइन करने के लिए देख सकते हैं ताकि आप इस स्थिति में खत्म न हों। – Thomas

उत्तर

1

मैं एक शुरुआत के रूप BFree द्वारा प्रदान की जवाब पसंद है, लेकिन मैं कुछ संशोधन करने चाहते हैं।

//Give it a better name; this isn't designed to be a general purpose class 
public class MyCompositeDisposable : IDisposable 
{ 
    public MyCompositeDisposable (string uri, int id, int innerid) 
    { 
     A = SomeFactory.GetA(uri); 
     B = A.GetB(id); 
     C = B.GetC(innerId); 
    } 

    //You can make A & B private if appropriate; 
    //not sure if all three or just C should be exposed publicly. 
    //Class names are made up; you'll need to fix. 
    //They should also probably be given more meaningful names. 
    public ClassA A{get;private set;} 
    public ClassB B{get;private set;} 
    public ClassC C{get;private set;} 

    public void Dispose() 
    { 
     A.Dispose(); 
     B.Dispose(); 
     C.Dispose(); 
    } 
} 

कर आप की तरह कुछ कर सकते हैं उसके बाद:

public bool GetSomeValueById(Guid innerId) 
{ 
    using(MyCompositeDisposable d = new MyCompositeDisposable(_uri, _id, innerId)) 
    { 
     return d.C.GetSomeValue(); 
    } 
} 

ध्यान दें कि MyCompositeDisposable संभावना ट्राई/अंत में निर्माता में ब्लॉक है और विधियों निपटान करने की आवश्यकता होगी ताकि निर्माण में त्रुटियों/विनाश ठीक से सुनिश्चित करें कि कुछ भी अनचाहे समाप्त नहीं होता है।

+0

इस तरह की कक्षा में इसे लपेटने का विचार मेरी आवश्यकताओं के लिए बिल्कुल सही है और मेरे सभी मामलों के लिए कोड डी-डुप्लिकेशन और लचीलापन का सही संतुलन प्रदान करता है, साथ ही यह चिंताओं को अलग करने में भी मदद करता है। यह सभी उत्तरों का सबसे अच्छा था। धन्यवाद। – ckittel

+0

यह बीएफरी के उत्तर के समान ही दोष है - सी के निर्माण के दौरान एक अपवाद ए और बी को डिस्पोजेक्ट नहीं करेगा। –

+1

@ डेविड बी मेरे पास जवाब के अंत में पहले से ही एक नोट था कि इस तरह की त्रुटि जांच की आवश्यकता है, लेकिन यह यहां जवाब में शामिल नहीं था। यदि ओपी के मामले में इसकी आवश्यकता है तो उसे पता है कि उसे इसे जोड़ने की जरूरत है। – Servy

0

यदि आपके Dispoable प्रकार सभी डिस्पोजेबल सदस्यों का सही ढंग से निपटान करते हैं, तो आपको केवल कथन का उपयोग करने की आवश्यकता होगी।

उदाहरण के लिए

, इस:

public bool GetSomeValueById(Guid innerId) 
{ 
    using (var a = SomeFactory.GetA(_uri)) 
    { 
     return a.GetSomeValue(); 
    } 
} 

class A : IDisposable 
{ 
    private a; 
    private b; 

    public A (B b, C c) 
    { 
    this.b = b; this.c = c; 
    } 

    public void Dispose() 
    { 
    Dispose(true); 
    } 

    protected void Dispose(bool disposing) 
    { 
    if (disposing) 
    { 
     b.Dispose(); 
     c.Dispose(); 
    } 
    } 
} 

आप होगा:

public bool GetSomeValueById(Guid innerId) 
{ 
    using (var a = SomeFactory.GetA(_uri)) 
    using (var b = a.GetB(_id)) 
    using (var c = b.GetC(innerId)) 
    { 
     return c.GetSomeValue(); 
    } 
} 

यदि प्रकार के बी और सी, और इसके निपटाने विधि में ख का एक निपटाए और ग के लिए किया था के सदस्यों को इस बन सकता है हालांकि, बी और सी को इंजेक्ट करने के लिए अपने कारखाने को संशोधित करना होगा।

+2

आपको किसी अन्य वर्ग द्वारा दी गई वस्तुओं का निपटान करते समय सावधान रहना चाहिए। क्या होगा यदि उस ऑब्जेक्ट पर एक से अधिक उदाहरण निर्भर हैं? निपटान आम तौर पर मालिक वर्ग की ज़िम्मेदारी होना चाहिए, और इस मामले में 'ए' का 'बी' और 'सी' नहीं है। – Thomas

+0

@ थॉमस उत्कृष्ट बिंदु। आम तौर पर, आपके पास बूलियन सीटीओ पैरामीटर भी होंगे जो दर्शाते हैं कि बी और सी का मालिक है या नहीं। – jrummell

1

आरएक्स ढांचे में एक वर्ग कहा जाता है CompositeDisposablehttp://msdn.microsoft.com/en-us/library/system.reactive.disposables.compositedisposable%28v=vs.103%29.aspx

भी अपने स्वयं के (यद्यपि बहुत नीचे छीन संस्करण) रोल करने के लिए कठिन नहीं होना चाहिए:

public class CompositeDisposable : IDisposable 
{ 
    private IDisposable[] _disposables; 

    public CompositeDisposable(params IDisposable[] disposables) 
    { 
     _disposables = disposables; 
    } 

    public void Dispose() 
    { 
     if(_disposables == null) 
     { 
      return; 
     } 

     foreach(var disposable in _disposables) 
     { 
      disposable.Dispose(); 
     } 
    } 
} 

फिर इस एक छोटे से क्लीनर दिखता है:

public void UpdateFromXml(Guid innerId, XDocument someXml) 
{ 
    var a = SomeFactory.GetA(_uri); 
    var b = a.GetB(_id); 
    var c = b.GetC(innerId); 
    using(new CompositeDisposable(a,b,c)) 
    { 
     var cWrapper = new SomeWrapper(c); 
     cWrapper.Update(someXml); 
    } 
} 
+2

क्या होगा यदि बी.जी.टी.टी. के दौरान कोई अपवाद होता है - मुझे नहीं लगता कि ए और बी ठीक होने पर ठीक से निपटान किया जाता है। –

1

आप हमेशा एक बड़ा संदर्भ बना सकते हैं जिसमें प्रबंधित करने के लिए कौन सी वस्तुओं को बनाया/डिस्पोजेड किया जाना चाहिए। फिर उस बड़े संदर्भ को बनाने के लिए एक विधि लिखें ...

public class DisposeChain<T> : IDisposable where T : IDisposable 
{ 
    public T Item { get; private set; } 
    private IDisposable _innerChain; 

    public DisposeChain(T theItem) 
    { 
     this.Item = theItem; 
     _innerChain = null; 
    } 

    public DisposeChain(T theItem, IDisposable inner) 
    { 
     this.Item = theItem; 
     _innerChain = inner; 
    } 

    public DisposeChain<U> Next<U>(Func<T, U> getNext) where U : IDisposable 
    { 
     try 
     { 
      U nextItem = getNext(this.Item); 
      DisposeChain<U> result = new DisposeChain<U>(nextItem, this); 
      return result; 
     } 
     catch //an exception occurred - abort construction and dispose everything! 
     { 
      this.Dispose() 
      throw; 
     } 
    } 

    public void Dispose() 
    { 
     Item.Dispose(); 
     if (_innerChain != null) 
     { 
      _innerChain.Dispose(); 
     } 
    } 
} 

फिर इसका इस्तेमाल:

public DisposeChain<DataContext> GetCDisposeChain() 
    { 
     var a = new DisposeChain<XmlWriter>(XmlWriter.Create((Stream)null)); 
     var b = a.Next(aItem => new SqlConnection()); 
     var c = b.Next(bItem => new DataContext("")); 

     return c; 
    } 

    public void Test() 
    { 
     using (var cDisposer = GetCDisposeChain()) 
     { 
      var c = cDisposer.Item; 
      //do stuff with c; 
     } 
    } 
संबंधित मुद्दे