c#-using(){}块中的yield return语句在执行之前进行处置

我已经编写了自己的自定义数据层以保留到特定文件,并使用自定义DataContext模式对其进行了抽象。

这全部基于.NET 2.0框架(对目标服务器有限制),因此即使其中一些看起来像LINQ-to-SQL,但事实并非如此! 我刚刚实现了类似的数据模式。

请参阅以下示例,以了解我无法解释的情况。

要获取Animal的所有实例,我可以这样做,并且效果很好

public static IEnumerable<Animal> GetAllAnimals() {
        AnimalDataContext dataContext = new AnimalDataContext();
            return dataContext.GetAllAnimals();
}

以及下面的AnimalDataContext()中的GetAllAnimals()方法的实现

public IEnumerable<Animal> GetAllAnimals() {
        foreach (var animalName in AnimalXmlReader.GetNames())
        {
            yield return GetAnimal(animalName);
        }
}

AnimalDataContext()实现IDisposable,因为我在那里有一个XmlTextReader,我想确保它能快速清理。

现在,如果我将第一个调用包装在using语句中,如下所示

public static IEnumerable<Animal> GetAllAnimals() {
        using(AnimalDataContext dataContext = new AnimalDataContext()) {
            return dataContext.GetAllAnimals();
        }
}

并在AnimalDataContext.GetAllAnimals()方法的第一行放置一个断点,并在AnimalDataContext.Dispose()方法的第一行放置另一个断点,然后执行...

Dispose()方法被称为FIRST,因此AnimalXmlReader.GetNames()给出“对象引用未设置为对象实例”的异常,因为在Dispose()中将AnimalXmlReader设置为null。

有任何想法吗? 我有一种预感,不允许在try-catch块中调用与收益率相关的收益,该收益在编译后使用有效表示...

2个解决方案
57 votes

当您调用List<Animal>时,直到在foreach循环中枚举返回的IEnumerable之前,它实际上不会执行任何代码。

在枚举IEnumerable之前,只要包装方法返回,就将处理dataContext。

最简单的解决方案是使wrapper方法也成为迭代器,如下所示:

public static IEnumerable<Animal> GetAllAnimals() {
    using (AnimalDataContext dataContext = new AnimalDataContext()) {
        foreach (var animalName in dataContext.GetAllAnimals()) {
            yield return GetAnimal(animalName);
        }
    }
}

这样,using语句将在外部迭代器中进行编译,并且仅在处置外部迭代器时才会处置它。

另一个解决方案是枚举包装器中的IEnumerable。 最简单的方法是返回List<Animal>,如下所示:

public static IEnumerable<Animal> GetAllAnimals() {
    using (AnimalDataContext dataContext = new AnimalDataContext()) {
        return new List<Animal>(dataContext.GetAllAnimals());
    }
}

请注意,这失去了延迟执行的好处,因此即使您不需要它们,它也会获得所有动物。

SLaks answered 2020-02-14T09:52:52Z
11 votes

原因是GetAllAnimals方法不返回动物的集合。 它返回一个能够一次返回动物的枚举器。

当您从using块内的GetAllAnimals调用返回结果时,您只需返回枚举数。 using块在方法退出之前处理数据上下文,并且此时枚举器还没有读取任何动物。 然后,当您尝试使用枚举器时,它无法从数据上下文中获取任何动物。

一种解决方法是使GetAllAnimals方法也创建一个枚举器。 这样,直到停止使用该枚举器,using块才会关闭:

public static IEnumerable<Animal> GetAllAnimals() {
   using(AnimalDataContext dataContext = new AnimalDataContext()) {
      foreach (Animal animal in dataContext.GetAllAnimals()) {
         yield return animal;
      }
   }
}
Guffa answered 2020-02-14T09:53:21Z
translate from https://stackoverflow.com:/questions/1539114/yield-return-statement-inside-a-using-block-disposes-before-executing