Return return inside a block using () {} Sets before execution

I wrote my own custom data layer to save it in a specific file, and I rendered it using the DataContext template.

All this is based on the .NET 2.0 Framework (predefined restrictions for the target server), so although some of them may look like LINQ-to-SQL, it doesn’t! I just implemented a similar data pattern.

See the example below, for example, for a situation that I still cannot explain.

To get all instances of Animal - I do this and it works great

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

And the implementation of the GetAllAnimals () method in AnimalDataContext () below

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

AnimalDataContext () implements IDisposable, because I have an XmlTextReader, and I want to make sure that it clears quickly.

Now, if I end the first call inside the using statement like this

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

and put a breakpoint on the first line of the AnimalDataContext.GetAllAnimals () method and another breakpoint on the first line of the AnimalDataContext.Dispose () method and do ...

The Dispose () method is called FIRST, so AnimalXmlReader.GetNames () provides an “object reference not set to an object instance” exception because AnimalXmlReader is null in Dispose () ???

Any ideas? I have a hunch that its connection with yield return cannot be called inside the try-catch block, which with the help of effectively represents, after compilation ...

+41
c # idisposable yield-return using
08 Oct '09 at 16:53
source share
2 answers

When you call GetAllAnimals , it actually does not execute any code until you list the returned IEnumerable in the foreach loop.

The DataContext is deleted as soon as the shell method returns before enumerating IEnumerable.

The simplest solution would be to make the wrapper method an iterator, for example:

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

Thus, the using statement will be compiled in the external iterator, and it will be deleted only when the external iterator is placed.

Another solution would be to list IEnumerable in the wrapper. The easiest way to do this is to return a List<Animal> , for example:

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

Please note that this loses the advantage of delayed execution, so it will receive all animals, even if you do not need them.

+47
Oct 08 '09 at 17:04
source share

The reason for this is that the GetAllAnimals method does not return a collection of animals. It returns an enumerator that is able to return the animal at a time.

When you return the result from a call to GetAllAnimals inside the usage block, you simply return the counter. The use block locks the data context until the method exits, and at that moment the enumerator has not yet read the animals. When you try to use a counter, it cannot get animals from the data context.

The workaround is for the GetAllAnimals method to also create an enumerator. Thus, the block in use will not be closed until you stop using this counter:

 public static IEnumerable<Animal> GetAllAnimals() { using(AnimalDataContext dataContext = new AnimalDataContext()) { foreach (Animal animal in dataContext.GetAllAnimals()) { yield return animal; } } } 
+9
Oct 08 '09 at 17:05
source share



All Articles