C# using(){}块中的yield return语句在执行之前进行处理
我已经编写了自己的自定义数据层来持久化到特定的文件,并使用自定义DataContext模式对其进行了抽象 这一切都是基于.NET2.0框架的(给定目标服务器的约束),因此,尽管其中一些可能看起来像LINQtoSQL,但事实并非如此!我刚刚实现了一个类似的数据模式 请参见下面的示例,以了解我无法解释的情况 要获取C# using(){}块中的yield return语句在执行之前进行处理,c#,.net-2.0,idisposable,using,yield-return,C#,.net 2.0,Idisposable,Using,Yield Return,我已经编写了自己的自定义数据层来持久化到特定的文件,并使用自定义DataContext模式对其进行了抽象 这一切都是基于.NET2.0框架的(给定目标服务器的约束),因此,尽管其中一些可能看起来像LINQtoSQL,但事实并非如此!我刚刚实现了一个类似的数据模式 请参见下面的示例,以了解我无法解释的情况 要获取动物的所有实例-我这样做,效果很好 public static IEnumerable<Animal> GetAllAnimals() { AnimalData
动物的所有实例
-我这样做,效果很好
public static IEnumerable<Animal> GetAllAnimals() {
AnimalDataContext dataContext = new AnimalDataContext();
return dataContext.GetAllAnimals();
}
AnimalDataContext
实现了IDisposable
,因为我在那里有一个XmlTextReader
,我想确保它很快得到清理
现在,如果我将第一个调用封装在一个using语句中,如下所示
public static IEnumerable<Animal> GetAllAnimals() {
using(AnimalDataContext dataContext = new AnimalDataContext()) {
return dataContext.GetAllAnimals();
}
}
公共静态IEnumerable GetAllAnimals(){
使用(AnimalDataContext dataContext=new AnimalDataContext()){
返回dataContext.GetAllAnimals();
}
}
并在AnimalDataContext.GetAllAnimals()
方法的第一行放置一个断点,在AnimalDataContext.Dispose()
方法的第一行放置另一个断点,然后执行
首先调用Dispose()
方法,以便AnimalXmlReader.GetNames()
给出“对象引用未设置为对象实例”异常,因为AnimalXmlReader
已在Dispose()
中设置为null
?
有什么想法吗?我有一种预感,它与
yield-return
相关,不允许在try-catch块内调用,而使用
有效地表示,一旦编译 当您调用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);
}
}
}
public static IEnumerable<Animal> GetAllAnimals() {
using (AnimalDataContext dataContext = new AnimalDataContext()) {
return new List<Animal>(dataContext.GetAllAnimals());
}
}
公共静态IEnumerable GetAllAnimals(){
使用(AnimalDataContext dataContext=new AnimalDataContext()){
foreach(dataContext.GetAllAnimals()中的var animalName){
动物(动物名称);
}
}
}
这样,using语句将在外部迭代器中编译,并且仅当外部迭代器被释放时才会被释放
另一种解决方案是在包装器中枚举IEnumerable。最简单的方法是返回一个列表
,如下所示:
public static IEnumerable<Animal> GetAllAnimals() {
using (AnimalDataContext dataContext = new AnimalDataContext()) {
foreach (var animalName in dataContext.GetAllAnimals()) {
yield return GetAnimal(animalName);
}
}
}
public static IEnumerable<Animal> GetAllAnimals() {
using (AnimalDataContext dataContext = new AnimalDataContext()) {
return new List<Animal>(dataContext.GetAllAnimals());
}
}
公共静态IEnumerable GetAllAnimals(){
使用(AnimalDataContext dataContext=new AnimalDataContext()){
返回新列表(dataContext.GetAllAnimals());
}
}
请注意,这将失去延迟执行的好处,因此即使您不需要它们,它也会获得所有动物。原因是GetAllAnimals方法不会返回动物集合。它返回一个能够一次返回一个动物的枚举数 当您从using块内的GetAllAnimals调用返回结果时,只需返回枚举数。using块在方法退出之前处理数据上下文,此时枚举器还没有读取任何动物。然后尝试使用枚举器时,它无法从数据上下文中获取任何动物 一种解决方法是让GetAllAnimals方法也创建一个枚举器。这样,在停止使用该枚举数之前,使用块将不会关闭:
public static IEnumerable<Animal> GetAllAnimals() {
using(AnimalDataContext dataContext = new AnimalDataContext()) {
foreach (Animal animal in dataContext.GetAllAnimals()) {
yield return animal;
}
}
}
公共静态IEnumerable GetAllAnimals(){
使用(AnimalDataContext dataContext=new AnimalDataContext()){
foreach(dataContext.GetAllAnimals()中的动物){
产回动物;
}
}
}
这基本上也是我遇到的问题之一,请看我的问题:谢谢SLaks-第二个选项成功了,它的代码行少,bug少!。该调用将进入位于WebForms项目表示层中的“AnimalDataContextAdapter”,特别是用于枚举集合,因此在延迟执行上没有真正的损失。我将其抽象为:永远不要在使用yield-return
或其他延迟执行方法的方法中接受IDisposable参数。使用yield-return
是安全的,并且在流上调用Dispose集合被完全枚举。这本质上意味着我只能使用yield return在内存集合中进行迭代,因为远程查询总是使用一些类来获取数据,这些类通常实现IDisposable。