yield(){}块内部的return语句在执行之前进行处理

我写了我自己的自定义数据层来坚持一个特定的文件,我用自定义的DataContext模式抽象它。

这是基于.NET 2.0框架(给定目标服务器的限制),所以即使其中一些看起来像LINQ到SQL,它不是! 我刚刚实现了一个类似的数据模式。

看下面的例子,我不能解释的情况的例子。

为了获得动物的所有实例 – 我这样做,它工作正常

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()给出了“对象引用未设置为对象的实例”exception,因为AnimalXmlReader已经在Dispose()中设置为空了。

有任何想法吗? 我有一个预感,它的产量返回不被允许在一个try-catch块内被调用,它使用有效的代表,一旦编译…

当你调用GetAllAnimals它并不会真正执行任何代码,直到你在foreach循环中枚举返回的IEnumerable为止。

枚举IEnumerable之前,只要包装器方法返回,dataContext就会被抛弃。

最简单的解决scheme是使包装方法也是一个迭代器,就像这样:

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

这样,using语句就会被编译到外层迭代器中,只有在外层迭代器被处理的时候才会被使用。

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

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

请注意,这会失去推迟执行的好处,所以即使你不需要它们,它也会得到所有的动物。

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

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

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

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