Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/290.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在Linq内部创建IDisposable-异常安全_C#_.net_Linq_Idisposable - Fatal编程技术网

C# 在Linq内部创建IDisposable-异常安全

C# 在Linq内部创建IDisposable-异常安全,c#,.net,linq,idisposable,C#,.net,Linq,Idisposable,我的设置类似于以下设置: class DisposableContainer : IDisposable { IEnumerable<DisposableObject> items; //Potential problem method public void Populate(IEnumerable<OtherThings> things) { items = things.Select(thing => new DisposableOb

我的设置类似于以下设置:

class DisposableContainer : IDisposable
{
  IEnumerable<DisposableObject> items;

  //Potential problem method
  public void Populate(IEnumerable<OtherThings> things)
  {
    items = things.Select(thing => new DisposableObject(thing)); 
  }

  //IDisposable Implementation
  private bool disposed = false;
  ~DisposableContainer()
  {
    Dispose(false);
  }

  public override void Dispose()
  {
    Dispose(true);

    GC.SuppressFinalize(this);
  }

  private void Dispose(bool disposing)
  {
    if (!disposed)
    {
      if (disposing)
      {
        if (items != null)
        {
          items.Select(item => item.Dispose());
        }
      }

      disposed = true;
    }
  }
}
类可处置容器:IDisposable
{
i数不清的项目;
//潜在问题法
公共空间填充(IEnumerable things)
{
items=things.Select(thing=>newdisposableobject(thing));
}
//IDisposable实现
私有布尔=假;
~DisposableContainer()
{
处置(虚假);
}
公共覆盖无效处置()
{
处置(真实);
总干事(本);
}
私有无效处置(bool处置)
{
如果(!已处置)
{
如果(处置)
{
如果(项!=null)
{
items.Select(item=>item.Dispose());
}
}
这是真的;
}
}
}
假设
DisposableObject
正确地实现了
IDisposable
,并且有一个接受类型为
OtherThing
的参数的构造函数

假设在
Populate
方法中,如果select调用的第三次迭代引发异常,那么成功创建的前两个
DisposableObject
会发生什么情况?我想他们会被泄露,不会被泄露


后续问题:在仍然使用Linq生成
IEnumerable
时,是否有一种方法可以安全地处理上述场景?我无法预见,但我想放弃这个想法,看看是否还有其他人有想法。

你的假设是正确的。已创建的可处置对象将无法正确处置

您可以通过使用保存已创建项的临时列表来修复它,并在需要时处理它们:

public void Populate(IEnumerable<OtherThings> things)
{
    var temp = new List<DisposableObject>();
    try
    {
        temp.AddRange(things.Select(otherThing => new DisposableObject(otherThing)));
        items = temp;
    }
    catch
    {
        foreach (var disposableObject in temp)
        {
            disposableObject.Dispose();
        }
        throw;
    }
}

如果您不使用像
ToArray
这样的立即执行操作,您的
Select
查询将永远不会执行。另外,您不应该在Linq查询中造成这样的副作用。
items.Select(item=>item.Dispose())是一种可怕的做法。使用foreach循环并将dispose包装在try-catch中以防出错。
items.Select(…)
可以改为
items.foreach(…)
@RufusL-Ah真的吗?我在找其中一个。。。谢谢。@Serge@RufusL
ForEach
不是Linq的一部分。您可以自己实现,也可以使用
MoreLinq
。在任何情况下,这都不能完全替换
Select
,因为它不返回任何值。它仅用于缩短常规的
foreach
public static IReadOnlyCollection<TDisposable> SelectDisposables<TItem, TDisposable>(
    this IEnumerable<TItem> enumerable, 
    Func<TItem,TDisposable> selector)
    where TItem : IDisposable
    where TDisposable : IDisposable
{
    var temp = new List<TDisposable>();
    try
    {
        temp.AddRange(enumerable.Select(selector));
        return temp;
    }
    catch
    {
        foreach (var disposable in temp)
        {
            disposable.Dispose();
        }
        throw;
    }
}
items = things.SelectDisposables(thing => new DisposableObject(thing));