Jus*_*ant 6 .net c# functional-programming idisposable using
我们的服务器应用程序有几个方法,按顺序调用,遍历20M行结果集并对其进行转换.此管道中的每个方法都存储200多兆字节的数据副本,可预测的RAM和GC性能会受到严重影响.
每种方法都遵循类似的模式:
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;
}
Run Code Online (Sandbox Code Playgroud)
然后在管道中调用这些方法,例如
var results1 = Step1(sourceData);
var results2 = Step2(results1);
var results3 = Step3(results2);
...
var finalResults = StepN (resultsNMinus1);
return finalResults; // final results
Run Code Online (Sandbox Code Playgroud)
我想将其转换为更加实用的解决方案,迭代原始源数据,而无需将整个数据集保存在RAM中.我想最终得到一个没有任何中间集合的最终结果列表.
如果在管道的每个阶段都没有需要设置,那么解决方案将很简单:只为每一行运行每个转换并仅存储最终结果.
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;
Run Code Online (Sandbox Code Playgroud)
但是今天,每个单独的管道步骤都有自己昂贵的设置和拆除过程,这些过程是通过using块强制执行的.
重构这些管道方法的好方法是什么,以确保设置/拆除代码?在伪代码中,我想最终得到这个:
将所有使用块组合成单个方法是不切实际的,因为每个步骤中的代码都很长并且是共享的,我不想在一个方法中重复该共享代码.
我知道我可以using用try/ 手动替换块finally,但是为多个资源手动替换它似乎比必要更难.
是否有更简单的解决方案,例如以智能方式使用using和yield合并?或者是否有一个很好的"多用途"类实现可以使这个协调的设置/拆卸过程变得容易(例如,它的构造函数接受返回IDisposable的函数列表,其Dispose()实现将确保清除所有内容)?
似乎这是一个比我已经想到的更聪明的模式,所以在重新发明轮子之前问这里.
我不确定您为什么要创建这么多一次性对象(您可以使用可生成的方法清理这些对象),但您可以创建一个扩展方法来为您清理此模式
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;
}
}
Run Code Online (Sandbox Code Playgroud)
你可以像这样使用它......
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);
Run Code Online (Sandbox Code Playgroud)
... 输出 ...
{ 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
Run Code Online (Sandbox Code Playgroud)
这是一个管道示例Aggregate...
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,
};
Run Code Online (Sandbox Code Playgroud)
...结果...
Disposed: 1/0
{ x = 0, r = 8 }
Disposed: 1/1
{ x = 1, r = 16 }
Disposed: 0/0
Run Code Online (Sandbox Code Playgroud)
...示例的测试类...
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; }
}
Run Code Online (Sandbox Code Playgroud)