2017-07-21 12 views
5

सक्षम करने के लिए ब्लॉक का उपयोग करके सी # को तोड़ने के लिए पैटर्न हमारे सर्वर ऐप में कई विधियां हैं, जिन्हें अनुक्रम में कहा जाता है जो 20 एम-पंक्ति परिणामसेट के माध्यम से पुनरावृत्त होते हैं और इसे बदलते हैं। इस पाइपलाइन में प्रत्येक विधि डेटा की 200+ मेगाबाइट प्रतिलिपि रखती है, अनुमानित रूप से खराब रैम और जीसी प्रदर्शन प्रभाव के साथ।कार्यात्मक प्रोग्रामिंग

public HugeCollection1 Step1 (SomeType sourceData) 
{ 
    var transformed = new List<RowType>; 
    using (var foo = InitializeSomethingExpensive(sourceData)) 
    { 
     foreach (var row in foo) 
     { 
      transformed.Add (TransformRow(row)); 
     } 
    } 
    return transformed; 
} 

तो इन तरीकों एक पाइप लाइन में कहा जाता है, उदाहरण के लिए:

प्रत्येक विधि एक समान पैटर्न का अनुसरण

var results1 = Step1(sourceData); 
var results2 = Step2(results1); 
var results3 = Step3(results2); 
... 
var finalResults = StepN (resultsNMinus1); 
return finalResults; // final results 

मैं एक अधिक कार्यात्मक समाधान में इस परिणत करना चाहते हैं कि कभी भी रैम में पूरे डाटासेट पकड़े बिना मूल स्रोत डेटा के माध्यम से दोहराता। मैं बिना किसी मध्यवर्ती संग्रह के अंतिम परिणामों की सूची समाप्त करना चाहता हूं।

यदि पाइपलाइन के प्रत्येक चरण में कोई सेटअप आवश्यक नहीं था, तो समाधान सरल होगा: प्रत्येक पंक्ति के लिए प्रत्येक रूपांतरण को चलाएं और केवल अंतिम परिणाम स्टोर करें।

var transformed = new List<SmallResult>; 
// TODO: How to set up and ensure teardown of the *other* pipeline steps? 
using (var foo = InitializeSomethingExpensive(sourceData)) 
{ 
    foreach (var row in foo) 
    { 
     object result = row; 
     foreach (var step in Pipeline) 
     { 
      result = step.Transform (result); 
     } 
     transformed.Add (result as SmallResult); 
    } 
} 
return transformed; 

लेकिन आज, उन अलग पाइप लाइन चरणों में से प्रत्येक अपनी ही महंगा सेटअप और आंसू डाउन प्रक्रिया एक using ब्लॉक के माध्यम से लागू किया जाता है कि है।

इन पाइपलाइन विधियों में से प्रत्येक को रिफैक्टर करने के लिए एक अच्छा पैटर्न क्या है ताकि सेटअप/टियरडाउन कोड होने की गारंटी हो?

  1. सेटअप सभी कदम प्रत्येक पंक्ति के माध्यम से
  2. लूप
  3. हर कदम
  4. अंत पाश
  5. सफाई सभी चरणों के माध्यम से पंक्ति रूपांतरण करें: छद्म कोड में, मैं इस के साथ खत्म करना चाहते हैं , कि सफाई की गारंटी हमेशा होता है
  6. वापसी (छोटे) परिणाम

यह Pra नहीं है सभी उपयोग किए गए ब्लॉक को एक ही विधि में संयोजित करने के लिए ctical क्योंकि इन चरणों में से प्रत्येक कोड में कोड लंबा और साझा किया गया है और मैं उस विधि को उस विधि में दोहराना नहीं चाहता हूं।

मुझे पता है कि मैं using ब्लॉक को try/finally के साथ मैन्युअल रूप से प्रतिस्थापित कर सकता हूं, लेकिन मैन्युअल रूप से एकाधिक संसाधनों के लिए ऐसा करना आवश्यक से कठिन लगता है।

क्या कोई आसान समाधान संभव है, उदा। एक स्मार्ट तरीके से using और yield का उपयोग कर? या क्या कोई अच्छा "बहु-प्रयोग" वर्ग कार्यान्वयन उपलब्ध है जो इस समन्वित सेटअप/टियरडाउन प्रक्रिया को आसान बनाता है (उदाहरण के लिए इसका कन्स्ट्रक्टर उन कार्यों की एक सूची स्वीकार करता है जो IDISposable लौटाते हैं और इसका निपटान() कार्यान्वयन सुनिश्चित करेगा कि सब कुछ साफ़ हो गया है)?

ऐसा लगता है कि यह एक ऐसा पैटर्न है जिसे मैंने पहले से ही चालाक किया है, इसलिए पहिया को फिर से आविष्कार करने से पहले यहां पूछना।

+0

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

+1

क्या प्रत्येक पाइपलाइन चरण को अपने 'foo' की आवश्यकता होती है (जो स्रोत डेटा की एक प्रति है?) – Blorgbeard

+0

निर्धारित अंतिमकरण उन क्षेत्रों में से एक है जहां सी ++ पनड प्रबंधित भाषाओं में से एक है। – hoodaticus

उत्तर

3

मुझे यकीन है कि तुम क्यों इतने सारे डिस्पोजेबल वस्तुओं का निर्माण कर रहे नहीं कर रहा हूँ (आप yieldable तरीकों के साथ इन साफ ​​कर सकते हैं), लेकिन आप के लिए आप

public static class ToolsEx 
{ 
    public static IEnumerable<T> EnumerateAndDispose<X, T>(this X input, 
             Func<X, IEnumerable<T>> func) 
     where X : IDisposable 
    { 
     using (var mc = input) 
      foreach (var i in func(mc)) 
       yield return i; 
    } 
} 

आप कर सकते हैं इस पद्धति को साफ करने के एक विस्तार विधि बना सकते हैं का उपयोग यह यह पसंद है ...

var query = from x in new MyClass(0, 0, 2).EnumerateAndDispose(i => i) 
      from y in new MyClass(1, x, 3).EnumerateAndDispose(i => i) 
      select new 
      { 
       x, 
       y, 
      }; 

foreach (var i in query) 
    Console.WriteLine(i); 

... उत्पादन ...

{ x = 0, y = 0 } 
{ x = 0, y = 1 } 
{ x = 0, y = 2 } 
Disposed: 1/0 
{ x = 1, y = 0 } 
{ x = 1, y = 1 } 
{ x = 1, y = 2 } 
Disposed: 1/1 
Disposed: 0/0 

यहाँ एक Pipeli है Aggregate साथ ne उदाहरण ...

var query = from x in new MyClass(0, 0, 2).EnumerateAndDispose(i => i) 
      let r = new MyClass(1, x, 3).EnumerateAndDispose(i => i) 
               .Aggregate(x, (a, i) => (a + i) * 2) 
      select new 
      { 
       x, 
       r, 
      }; 

... और परिणाम ...

Disposed: 1/0 
{ x = 0, r = 8 } 
Disposed: 1/1 
{ x = 1, r = 16 } 
Disposed: 0/0 

... उदाहरण के लिए परीक्षण वर्ग ...

public class MyClass : IEnumerable<int>, IDisposable 
{ 

    public MyClass(int set, int set2, int size) 
    { 
     this.Size = size; 
     this.Set = set; 
     this.Set2 = set2; 
    } 

    public IEnumerator<int> GetEnumerator() 
    { 
     foreach (var i in Enumerable.Range(0, this.Size)) 
      yield return i; 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 

    public void Dispose() 
    { 
     Console.WriteLine("Disposed: {0}/{1}", this.Set, this.Set2); 
    } 

    public int Size { get; private set; } 
    public int Set { get; private set; } 
    public int Set2 { get; private set; } 
} 
+0

बीटीडब्ल्यू, आपके पास यह है कि आप अपनी पाइपलाइन को 'एग्रेगेट (...)' के साथ बदल सकते हैं। –

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