是否有一个use / Dispose语法会更渴望进行处理,但与链接一样安全?

时间:2018-11-08 03:50:37

标签: c# garbage-collection using idisposable readability

如果我正在执行以下操作:

using (var foo = bar.GetFoo())
using (var blat = new Blat(foo))
using (var bonk = Bonk.FromBlat(blat))
using (var bork = Bob.FromBip(bonk))
{
    var target = bork.ToArray(); // I GOT THE TARGET
}

这是非常安全且易读的,但是我的理解是,只有在最后一行之后,整个事情才会展开,以处置所有内容。假设fooblatbonk仅用于创建bork的一行,是否有一种好方法(“好”当然是主观的)来处理他们习惯于尽快释放资源后的那一刻?我真的很喜欢使用链的安全性和可读性,我很好奇是否还有另一种方法可以做到这一点。

2 个答案:

答案 0 :(得分:1)

您可以编写一种方法来使用产生值的IDisposable。这里的部分问题是,using语句作为一个语句不会产生值,因此很难链接:

public static TResult Use<TSource, TResult>(this TSource source, Func<TSource, TResult> selector)
    where TSource : IDisposable
{
    using (source)
        return selector(source);
}

这使您可以编写:

var target = bar.GetFoo()
    .Use(foo => new Blat(foo))
    .Use(blat => Bonk.FromBlat(blat))
    .Use(bonk => Bob.FromBonk(bonk))
    .Use(bork => bork.ToArray());

请注意,此Use方法从外部接受IDisposable,但将其自身处置,因此,如果您在Use上调用IDisposable,则仍然保持范围,您甚至可能在处理完它后仍然使用它,这应该是 错误。在这种情况下,我们永远不会抓住一次性物品,但是其他人可能不知道他们不应该这样做。

如果您想解决这个问题,可以编写一个将一次性值链接在一起的函数。通过将所有功能整合到一个功能中,您可以确保没有任何一次性产品可以非常轻松地从其中“漏出”:

public static TResult ChainDisposables<T1, T2, T3, T4, TResult>(Func<T1> firstFunc,
    Func<T1, T2> secondFunc,
    Func<T2, T3> thirdFunc,
    Func<T3, T4> fourthFunc,
    Func<T4, TResult> resultFunc)
    where T1 : IDisposable
    where T2 : IDisposable
    where T3 : IDisposable
    where T4 : IDisposable
{
    return firstFunc()
        .Use(secondFunc)
        .Use(thirdFunc)
        .Use(fourthFunc)
        .Use(resultFunc);
}

(只需按照示例,您需要为链中每个不同数量的对象创建重载。)

哪位让你写:

var target = ChainDisposables(() => bar.GetFoo(),
    foo => new Blat(foo),
    blat => Bonk.FromBlat(blat),
    bonk => Bob.FromBonk(bonk),
    bork => bork.ToArray());

答案 1 :(得分:0)

版本1和版本2在功能上并不等效,因为处理顺序应在您的伪代码中颠倒为2:

Foo foo = null;
Blat blat = null;
Bonk bonk = null;
Bork bork = null;
try
{
    foo = bar.GetFoo();
    blat = new Blat(foo);
    bonk = Bonk.FromBlat(blat);
    bork = Bob.FromBip(bonk);
    var target = bork.Whatever; // I GOT THE TARGET
}
finally
{   // the disposal should happen in reverse order to instantiation
    bork?.Dispose();
    bonk?.Dispose();
    blat?.Dispose();
    foo?.Dispose();
}

看到错误的顺序后,您可能会理解为什么使用链接的using语句可以确保事情以正确的顺序发生,而不是依靠您来正确处理

如果编译器可以确保按正确的顺序完成操作-请让编译器为您执行-减少出错的机会