Nasza aplikacja serwerowa ma kilka metod, wywoływanych w sekwencji, które przechodzą przez zestaw wyników w wierszu 20-wierszowym i przekształcają go. Każda metoda w tym potoku przechowuje 200-megabajtową kopię danych, z przewidywalnym złym oddziaływaniem pamięci RAM i GC.Wzór do rozbicia C# za pomocą bloków, aby umożliwić programowanie funkcjonalne
Każdy sposób jest podobny wzór:
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;
}
Następnie te metody nazywane są w rurociągu, np
var results1 = Step1(sourceData);
var results2 = Step2(results1);
var results3 = Step3(results2);
...
var finalResults = StepN (resultsNMinus1);
return finalResults; // final results
Chciałbym przekształcić to do roztworu bardziej funkcjonalne, które iteracji oryginalnych danych źródłowych bez posiadania całego zestawu danych w pamięci RAM. Chcę skończyć z listą ostatecznych wyników bez żadnych kolekcji pośrednich.
Jeśli na każdym etapie rurociągu nie było żadnych ustawień, rozwiązanie byłoby proste: wystarczy przeprowadzić każdą transformację dla każdego wiersza i zapisać tylko wynik końcowy.
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;
dzisiaj Ale każda z tych oddzielnych etapach rurociągu ma swój własny proces kosztowny konfiguracji i rozdarcie w dół, który jest egzekwowany przez using
bloku.
Jaki jest dobry wzór do refaktorowania każdej z tych metod rurociągu, aby zagwarantować, że kod instalacji/zagospodarowania zostanie zagwarantowany? Pseudo-kod, to, że jak się w końcu z tym:
- instalacji wszystkie etapy
- pętli każdego rzędu
- Transform wiersz po każdym etapie
- Koniec pętli
- czyści wszystkie etapy , gwarantując, że porządki zawsze dzieje
- Return (mały) wynika
To nie jest pra ctical, aby połączyć wszystkie bloki używające w jedną metodę, ponieważ kod w każdym z tych kroków jest długi i udostępniony i nie chcę powtarzać tego wspólnego kodu w jednej metodzie.
Wiem, że można ręcznie zastąpić blok using
z try
/finally
, ale robi to ręcznie dla wielu zasobów wydaje się trudniejsze niż to konieczne.
Czy istnieje prostsze rozwiązanie, np. przy użyciu using
i yield
razem w inteligentny sposób? Czy jest dostępna dobra implementacja klasy "multi-using", która ułatwia ten skoordynowany proces instalacji/rozpadu (np. Jego konstruktor akceptuje listę funkcji, które zwracają IDisposable, a jego implementacja Dispose() zapewniłaby, że wszystko zostanie oczyszczone)?
Wygląda na to, że ktoś mądrzejszy ode mnie już się zorientował, więc zapytaj tutaj przed ponownym wynalezieniem koła.
jestem z trudem tłumacząc twoją uwagę na wielokrotne używanie, ponieważ nie widzę nic więcej niż jeden, używając bloku w twoim kodzie. Wydajność ma wątpliwą wartość, ponieważ osoba dzwoniąca nie musi nigdy przenosić się na koniec sekwencji, czyli tam, gdzie w naturalny sposób nazwiesz Dispose. – hoodaticus
Czy każdy etap rurociągu wymaga własnego 'foo' (który jest kopią danych źródłowych?) – Blorgbeard
Deterministyczna finalizacja jest jednym z tych obszarów, w których C++ obsługuje języki zarządzane. – hoodaticus