using(){}块内的yield return语句在执行之前处置

时间:2009-10-08 16:53:51

标签: c# .net-2.0 idisposable using yield-return

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

这都是基于.NET 2.0 Framework(给定目标服务器的约束),所以尽管其中一些可能看起来像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,我想确保它能够快速清理。

现在,如果我将第一个调用包含在像这样的使用语句

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

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

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

有什么想法吗?我有一个预感,它与 yield return 相关,不允许在try-catch块中调用,使用有效地表示,一旦编译...

2 个答案:

答案 0 :(得分:56)

当你调用GetAllAnimals时,它实际上并不执行任何代码,直到你在foreach循环中枚举返回的IEnumerable。

在枚举IEnumerable之前,一旦包装器方法返回,就会处理dataContext。

最简单的解决方案是将包装器方法设置为迭代器,如下所示:

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());
    }
}

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

答案 1 :(得分:11)

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

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

解决方法是使GetAllAnimals方法也创建一个枚举器。这样,在您停止使用该枚举器之前,不会关闭使用块:

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