3

If so, on what .NET Framework versions is it supported?

I have tested this on .NET Framework 4.0 and it works fine:

using System;
using System.Collections.Generic;

public class TestClass
{
    public IEnumerable Defer()
    {
        yield return 1;
        yield return 2;
        yield return 3;
    }
}
Edgar Gonzalez
  • 1,862
  • 1
  • 15
  • 19

3 Answers3

4

Yes, it is supported ever since the yield keyword was. The only difference is that it's more or less IEnumerable<object>, which might lead to inefficiencies if it has to do boxing. Other than that, it's exactly the same.

Mike Caron
  • 14,351
  • 4
  • 49
  • 77
  • 1
    Deferred execution has nothing to do with `yield`. `yield` is just syntax sugar. There is no reason why you cannot have deferred execution in .NET 1.0 using `IEnumerable`. – leppie Jan 07 '11 at 04:51
  • True. But, the OP's example specifically uses yield which would indicate he's not targeting 1.0. (Is anyone these days?) – Mike Caron Jan 08 '11 at 04:57
  • Thank you for reminding me that yield is just syntactic sugar. As for using 1.0, I agree – Edgar Gonzalez Jan 08 '11 at 07:49
1

As the yield keywords are reduced to compiler trickery, presumably this should work. It certainly works for the 2.0 runtime; I'd hesitate to make any statements about 1.1, however.

Ben
  • 6,023
  • 1
  • 25
  • 40
1

The non-generic IEnumerable does not implement IDisposable. It may be that VB.Net and C# will duck-type either IDisposable or the .Dispose() method when using an enumerator that does not support IEnumerable(Of T), but one can certainly not rely upon all consumers of the non-generic IEnumerable to do so. If the consumer of an enumerable does not properly .Dispose() it, execution of the enumerator, including explicit or implicit finally clauses, will be abandoned.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • I'm looking for more information on this; I just can't wrap my head around this apparent glaring design flaw with IEnumerable. I was reading this article, https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About, which states under "Backwards Compatibility" that client code expecting non-generic IEnumerator's does not properly dispose of the iterator. What sort of error could this cause? – TamaMcGlinn Mar 20 '18 at 14:11
  • @TamaMcGlinn: Consider the case of a collection which is used in multiple threads. If all code that enumerates the collection calls `GetEnumerator`, quickly enumerates the collection, and then calls `Dispose` on that enumerator, then having `GetEnumerator` acquire a lock and having `Dispose` release it might work well. If `Dispose` never gets called, however, code needing to use the collection might get blocked indefinitely. One could perhaps work around this by having the collection hold a reference which will identify the active enumerator (if one exists), and have code that would... – supercat Mar 21 '18 at 14:42
  • ...check whether the lock is being held by an enumerator and--if so--start by waiting a little while for the lock. If that fails, the code could then engage a secondary lock [which the enumerator would have to acquire and release each time it actually gets an element], enumerate the remainder of the collection to a list, tell the enumerator to fetch remaining data from that list, and then steal the primary lock. That would add a lot of complexity, however, which wouldn't be needed if users of the enumerator adhere to proper discipline. – supercat Mar 21 '18 at 14:45
  • Understood. So does this mean that having IEnumerator inherit from IEnumerator breaks the Liskov substitution principle? You can't just pass someone an IEnumerator when they specify in the interface that they accept all IEnumerator's, because then such interfaces won't dispose of the IEnumerator, which could cause a locking problem as you've described. – TamaMcGlinn Mar 26 '18 at 07:30
  • @TamaMcGlinn: The problem fundamentally is `IEnumerable.GetEnumerator` may return any of three things: "object that can be safely abandoned but doesn't implement `IDisposable`", "object that can be abandoned only calling `Dispose`", or "object that can be safely abandoned with or without calling `Dispose`, and there's no nice way for client code to handle all three. `IEnumerable` guarantees it won't return objects of the first type. The original designers of `IEnumerable` may have expected that it would never return objects of the second type, but that was never realistic. – supercat Mar 26 '18 at 15:13
  • @TamaMcGlinn: IMHO, the real problem is that both `IEnumerable` and `IEnuerable`, along with `IEnumerator` and `IEnumerator` omit features and properties which all implementations should be able to support, e.g. "will calling `GetEnumerator` yield an object that may be safely abandoned", "do you know how many items you contain", or "will you always report the same contents". This should be handled by using a property, rather than having different interfaces, so as to allow enumerators to be wrapped by other enumerators without an explosion of wrapper types. – supercat Mar 26 '18 at 16:26