[Mono-bugs] [Bug 517415] New: IEnumerator<T>.Current for List<T> Throws InvalidOperationException After MoveNext() returns false

bugzilla_noreply at novell.com bugzilla_noreply at novell.com
Mon Jun 29 10:54:24 EDT 2009


http://bugzilla.novell.com/show_bug.cgi?id=517415


           Summary: IEnumerator<T>.Current for List<T> Throws
                    InvalidOperationException After MoveNext() returns
                    false
    Classification: Mono
           Product: Mono: Class Libraries
           Version: 2.4.x
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: Normal
          Priority: P5 - None
         Component: System
        AssignedTo: mono-bugs at lists.ximian.com
        ReportedBy: stewart at medit.fr
         QAContact: mono-bugs at lists.ximian.com
          Found By: ---


User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.11)
Gecko/2009060215 Firefox/3.0.11

The behaviour of IEnumerator<T>.Current for List<T> differs from that of
Microsoft's .Net and MSDN.

Reproducible: Always

Steps to Reproduce:
1. Create and populate List<T> collection.
2. Access IEnumerator<T> with GetEnumerator();
3. Repeat MoveNext() until it returns false.
4. Access Current property.
Actual Results:  
Unhandled Exception: System.InvalidOperationException: Operation is not valid
due to the current state of the object
  at System.Collections.Generic.List`1+Enumerator[System.Object].get_Current ()
[0x00000] 
  at (wrapper static-rgctx-invoke)

Expected Results:  
Expected the return value from Current to be "undefined".  Did not expect
System.InvalidOperationException exception.

While the behaviour in Mono (2.2 and 2.4 tested) might be sensible, it differs
from that observed with Microsoft's .Net 2.0.

According to MSDN, http://msdn.microsoft.com/en-us/library/58e146b7.aspx (see
Remarks section within) Current is undefined if the last call to MoveNext()
returned false. In practise, on .Net 2.0 at least, the appropriate type's
default value is returned.

A pair of NUnit test cases are given below. One demonstrates that Current
throws an exception on Mono, and the other demonstrates that the default value
for the underlying collection's type is returned.

using System;
using System.Collections.Generic;

using NUnit.Framework;

namespace Fr.Medit.Tests
{
  /// <summary>
  /// Test for differences between Mono and MS .Net.
  /// </summary>
  [TestFixture]
  public class MonoVersusDotNet
  {
    /// <summary>
    /// Tests the List{T}.Enumerator{T}.Current behaviour
    /// </summary>
    /// <remarks>
    /// This passes on Mono 2.2 and 2.4, but fails on .Net 2.0
    /// </remarks>
    [Test]
    public void
TestGenericListEnumeratorCurrentThrowsInvalidOperationExceptionOnMono()
    {
      List<int> myList = new List<int>();
      myList.Add(99);

      IEnumerator<int> enumerator = myList.GetEnumerator();

      while (enumerator.MoveNext())
      {
        Assert.AreEqual(99, enumerator.Current);
      }

      AssertExceptionIsInvalidOperationException(delegate() { int current =
enumerator.Current; });
    }

    /// <summary>
    /// Define a void, parameterless delegate
    /// </summary>
    public delegate void MethodInvoker();

    /// <summary>
    /// Asserts that the exception thrown is an InvalidOperationException.
    /// </summary>
    /// <param name="method">The method to test.</param>
    public static void AssertExceptionIsInvalidOperationException(MethodInvoker
method)
    {
      try
      {
        method.DynamicInvoke(null);
        Assert.Fail("Did not throw an exception");
      }
      catch (AssertionException ae)
      {
        throw ae;
      }
      catch (InvalidOperationException ae)
      {
        return;
      }
      catch (Exception ae)
      {
        throw new AssertionException("Did not throw an exception of expected
type");
      }
    }

    /// <summary>
    /// Tests the List{T}.Enumerator{T}.Current behaviour
    /// </summary>
    /// <remarks>
    /// This passes on .Net 2.0, but fails on Mono 2.2 and 2.4
    /// </remarks>
    [Test]
    public void TestGenericListEnumeratorCurrentReturnsDefaultOnDotNet()
    {
      List<int> myList = new List<int>();
      myList.Add(99);

      IEnumerator<int> enumerator = myList.GetEnumerator();

      while (enumerator.MoveNext())
      {
        Assert.AreEqual(99, enumerator.Current);
      }

      Assert.AreEqual(default(int), enumerator.Current);
    }
  }
}

I propose the following patch to fix the behaviour in Mono.  At
mcs/class/corlib/System.Collections.Generic/List.cs:

Replace this:

                        public T Current {
                                get {
                                        if (idx < 0)
                                                throw new
InvalidOperationException ();

                                        return l.data [l.size - 1 - idx];
                                }
                        }

With this:

                        public T Current {
                                get {
                                        return (idx < 0) ? default (T) : l.data
[l.size - 1 - idx];
                                }
                        }

-- 
Configure bugmail: http://bugzilla.novell.com/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the QA contact for the bug.
You are the assignee for the bug.


More information about the mono-bugs mailing list