是否有一种 using/Dispose 语法更容易处理但与链接一样安全?
Is there a using/Dispose syntax that will be more eager to dispose but just as safe as chaining?
如果我正在执行以下操作:
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
}
它非常安全且可读,但我的理解是,只有在最后一行之后,整个事情才会展开以处理所有内容。鉴于foo
、blat
、bonk
只用单行来创建bork
,有没有好的方法("good"当然是主观的)尽快处理它们以释放资源?我真的很喜欢 using 链的安全性和可读性,我只是好奇是否有另一种方法可以做到这一点。
版本 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
语句可以确保事情以正确的顺序发生,而不是依赖您来获得正确的结果
如果编译器可以确保事情按正确的顺序完成 - 让编译器为你做 - 更少的错误机会
您可以编写一个方法来使用生成值的 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
,但会自行处理它,因此如果您在 IDisposable
上调用 Use
如果你仍然在范围内,即使在它被处理后你仍然可以使用它,should 错误。在这种情况下,我们永远不会抓住一次性物品,但其他人可能不知道他们不应该这样做。
如果您想解决这个问题,您可以编写一个将一次性值链接在一起的函数。通过把它全部放在一个函数中,你可以确保没有一次性 "leak" 很容易地从它出来:
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());
3 年后我可以报告说,2019 年 9 月发布的 C# 8 添加了内联 using
语句。我的问题是“我很好奇是否还有另一种方法可以做到这一点。”现在还有另一种方法。
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement
我不能说处理是否更急切地发生了,但对我来说它看起来更好,括号和圆括号更少,而且在某些情况下还避免了嵌套。
如果我正在执行以下操作:
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
}
它非常安全且可读,但我的理解是,只有在最后一行之后,整个事情才会展开以处理所有内容。鉴于foo
、blat
、bonk
只用单行来创建bork
,有没有好的方法("good"当然是主观的)尽快处理它们以释放资源?我真的很喜欢 using 链的安全性和可读性,我只是好奇是否有另一种方法可以做到这一点。
版本 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
语句可以确保事情以正确的顺序发生,而不是依赖您来获得正确的结果
如果编译器可以确保事情按正确的顺序完成 - 让编译器为你做 - 更少的错误机会
您可以编写一个方法来使用生成值的 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
,但会自行处理它,因此如果您在 IDisposable
上调用 Use
如果你仍然在范围内,即使在它被处理后你仍然可以使用它,should 错误。在这种情况下,我们永远不会抓住一次性物品,但其他人可能不知道他们不应该这样做。
如果您想解决这个问题,您可以编写一个将一次性值链接在一起的函数。通过把它全部放在一个函数中,你可以确保没有一次性 "leak" 很容易地从它出来:
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());
3 年后我可以报告说,2019 年 9 月发布的 C# 8 添加了内联 using
语句。我的问题是“我很好奇是否还有另一种方法可以做到这一点。”现在还有另一种方法。
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement
我不能说处理是否更急切地发生了,但对我来说它看起来更好,括号和圆括号更少,而且在某些情况下还避免了嵌套。