Hi,

We could add IEnumerable<T> to TermEnum without affecting the current api. It would allow things like foreach(var t in reader.Terms()). Dispose _will_ be called by a foreach statement if the IEnumerator<T> implements IDisposable (not the IEnumerable<T>). However, the linq extension methods are at bit more quirky. ToArray<T>(...) will create an internal Buffer<T> which will foreach (and dispose) as previously described. However, if the enumerable is also an ICollection<T>, then there will be no foreach, but a optimized call to ICollection<T>.CopyTo, and thus no call do IDisposable.Dispose. Where<T>(...) will (assuming we're not an T[] or List<T>) create an internal filtering iterator which will dispose our iterator.

The linq extension methods seems to dispose the iterator in all scenarios when one is created. I think we're in the clear as long as we don't implement ICollection<T> or derive from List<T>.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication {
    public static class Program {
        public static void Main() {
            Console.WriteLine("Normal foreach:");
            foreach (var t in Terms()) {
                Console.WriteLine("  " + t);
            }

            Console.WriteLine("System.Linq:");
            var arr = Terms().ToArray();
            foreach (var t in arr) {
                Console.WriteLine("  " + t);
            }

            Console.ReadLine();
        }

        // Somewhat simplified version of IndexReader.Terms()
        public static TermEnum Terms() {
            return new TermEnum();
        }

        public class TermEnum : IEnumerable<String> {
private readonly String[] _texts = new[] { "term1", "term2", "term3" };
            private Int32 _position = -1;

            #region Existing api

            public Boolean Next() {
                return (++_position < _texts.Length);
            }

            public String Text() {
                return _texts[_position];
            }

            #endregion

            public IEnumerator<String> GetEnumerator() {
                return new TermEnumEnumerator(this);
            }

            IEnumerator IEnumerable.GetEnumerator() {
                return GetEnumerator();
            }
        }

        public class TermEnumEnumerator : IEnumerator<String> {
            private readonly TermEnum _owner;

            public TermEnumEnumerator(TermEnum owner) {
                _owner = owner;
            }

            public void Dispose() {
                Console.WriteLine("TermEnumEnumerator.Dispose()");
            }

            public Boolean MoveNext() {
                return _owner.Next();
            }

            void IEnumerator.Reset() {
                throw new NotImplementedException();
            }

            public String Current {
                get { return _owner.Text(); }
            }

            Object IEnumerator.Current {
                get { return Current; }
            }
        }
    }
}





On 2012-06-08 21:25, Andy Pook wrote:
If we don't want to add IEnumerable (though it seems that IEnumerable could
be added in parallel with the existing pattern) could we add a bunch of
extension methods?
Something like the following...

{noformat}
public static class LuceneExtensions
{
public static IEnumerable<Term>  GetEnumerable(this TermEnum termEnum)
{
yield return termEnum.Term();
while (termEnum.Next())
yield return termEnum.Term();
}
}
{noformat}

Then you can...
{noformat}
foreach(var e in myTernEnum.GetEnumerable())
{
     // do stuff with e
}
{noformat}

Not as elegant as a direct implementation but gives easy enough access to
foreach sematics.


The second option is to realize that you don't need to explicitly implement
IEnumerable. You just need a GetEnumerator method.
So just add...

{noformat}
public IEnumerable<Term>  GetEnumerator()
{
yield return Term();
while (Next())
yield return Term();
}
{noformat}

Now you get nice foreach sematics without even mentioning IEnumerable.
Compiler magic is your friend :-)

BTW: Dispose() is only called automatically when exiting a using block.
Exiting a foreach will not.

Cheers,
   Andy

On 24 January 2012 06:37, Christopher Currens (Created) (JIRA)<
j...@apache.org>  wrote:

Convert Java Iterator classes to implement IEnumerable<T>
---------------------------------------------------------

                 Key: LUCENENET-469
                 URL: https://issues.apache.org/jira/browse/LUCENENET-469
             Project: Lucene.Net
          Issue Type: Sub-task
          Components: Lucene.Net Contrib, Lucene.Net Core
    Affects Versions: Lucene.Net 2.9.4, Lucene.Net 3.0.3, Lucene.Net 2.9.4g
         Environment: all
            Reporter: Christopher Currens
             Fix For: Lucene.Net 3.0.3


The Iterator pattern in Java is equivalent to IEnumerable in .NET.
  Classes that were directly ported in Java using the Iterator pattern,
cannot be used with Linq or foreach blocks in .NET.

{{Next()}} would be equivalent to .NET's {{MoveNext()}}, and in the below
case, {{Term()}} would be as .NET's {{Current}} property.  In cases as
below, it will require {{TermEnum}} to become an abstract class with
{{Term}} and {{DocFreq}} properties, which would be returned from another
class or method that implemented {{IEnumerable<TermEnum>}}.

{noformat}
        public abstract class TermEnum : IDisposable
        {
                public abstract bool Next();
                public abstract Term Term();
                public abstract int DocFreq();
                public abstract void  Close();
                public abstract void Dispose();
        }
{noformat}

would instead look something like:

{noformat}
        public class TermFreq
        {
                public abstract Term { get; }
                public abstract int { get; }
        }

        public abstract class TermEnum : IEnumerable<TermFreq>, IDisposable
        {
                // ...
        }
{noformat}

Keep in mind that it is important that if the class being converted
implements {{IDisposable}}, the class that is enumerating the terms (in
this case {{TermEnum}}) should inherit from both {{IEnumerable<T>}} *and*
{{IDisposable}}.  This won't be any change to the user, as the compiler
automatically calls {{IDisposable}} when used in a {{foreach}} loop.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA
administrators:
https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira



Reply via email to