Home > other >  Issue replicating C# enumerator interfaces
Issue replicating C# enumerator interfaces

Time:08-09

For my project I need to replicate C# enumerator interface hierarchy. I proceeded by simply copy&paste&rename from VS F12:

  public interface IMyEnumerator {
    object Current { get; }
    bool MoveNext();
    void Reset();
  }

  public interface IMyEnumerator<out T> : IMyEnumerator, IDisposable {
    T Current { get; }
  }

  public interface IMyEnumerable {
    IMyEnumerator GetEnumerator();
  }

  public interface IMyEnumerable<out T> : IMyEnumerable {
    IMyEnumerator<T> GetEnumerator();
  }

  public class MyContainer : IMyEnumerable<T>, IMyEnumerable {
    ...
  }
}

I get warnings about IMyEnumerator<out T>.Current hiding IMyEnumerator.Current and IMyEnumerable<out T>.GetEnumerator hiding IMyEnumerable.GetEnumerator with a recommndation to add the new keyword.

Should I be adding the new keyword in this case ? I am a bit unsure about the implications as the matching sources for IEnumerator and IEnumerable (at least as seen from VS F12) do not have the new keyword, see:

namespace System.Collections {
  //
  // Summary:
  //     Supports a simple iteration over a non-generic collection.
  public interface IEnumerator {
    //
    // Summary:
    //     Gets the element in the collection at the current position of the enumerator.
    //
    // Returns:
    //     The element in the collection at the current position of the enumerator.
    object Current { get; }

    //
    // Summary:
    //     Advances the enumerator to the next element of the collection.
    //
    // Returns:
    //     true if the enumerator was successfully advanced to the next element; false if
    //     the enumerator has passed the end of the collection.
    //
    // Exceptions:
    //   T:System.InvalidOperationException:
    //     The collection was modified after the enumerator was created.
    bool MoveNext();
    //
    // Summary:
    //     Sets the enumerator to its initial position, which is before the first element
    //     in the collection.
    //
    // Exceptions:
    //   T:System.InvalidOperationException:
    //     The collection was modified after the enumerator was created.
    void Reset();
  }
  //
  // Summary:
  //     Exposes an enumerator, which supports a simple iteration over a non-generic collection.
  public interface IEnumerable {
    //
    // Summary:
    //     Returns an enumerator that iterates through a collection.
    //
    // Returns:
    //     An System.Collections.IEnumerator object that can be used to iterate through
    //     the collection.
    IEnumerator GetEnumerator();
  }
}

namespace System.Collections.Generic {
  //
  // Summary:
  //     Supports a simple iteration over a generic collection.
  //
  // Type parameters:
  //   T:
  //     The type of objects to enumerate.
  public interface IEnumerator<out T> : IEnumerator, IDisposable {
    //
    // Summary:
    //     Gets the element in the collection at the current position of the enumerator.
    //
    // Returns:
    //     The element in the collection at the current position of the enumerator.
    T Current { get; }
  }

  //
  // Summary:
  //     Exposes the enumerator, which supports a simple iteration over a collection of
  //     a specified type.
  //
  // Type parameters:
  //   T:
  //     The type of objects to enumerate.
  public interface IEnumerable<out T> : IEnumerable {
    //
    // Summary:
    //     Returns an enumerator that iterates through the collection.
    //
    // Returns:
    //     An enumerator that can be used to iterate through the collection.
    IEnumerator<T> GetEnumerator();
  }
}

CodePudding user response:

If you refer to the source code, you'll see that IEnumerator<T> does actually use the new keyword:

public interface IEnumerator<out T> : IDisposable, IEnumerator
{
    // Returns the current element of the enumeration. The returned value is
    // undefined before the first call to MoveNext and following a
    // call to MoveNext that returned false. Multiple calls to
    // GetCurrent with no intervening calls to MoveNext
    // will return the same object.
    new T Current
    {
        get;
    }
}

Sources:

CodePudding user response:

The reference source on https://referencesource.microsoft.com shows that both IEnumerator<T>.Current and IEnumerable<T>.GetEnumerator() have the new modifier.

Visual Studio doesn't show it possibly because you are viewing the dll metadata, which only has the method signatures, and that doesn't include the new modifier. You'd need to see the decompiled code to see the new modifier. See also this question for a similar question about Visual Studio not showing everything.

There is really not much of a deep implication here. It is just warning you that IEnumerator<T>.Current and IEnumerable.Current are two different properties, rather than the former overriding the latter. If you are aware of that (which I think you are already), add a new to show that you are aware.

Just in case you don't understand what I mean by "IEnumerator<T>.Current and IEnumerable.Current are two different properties", think about how you would implement your interfaces. Here is a very minimal implementation:

public class MyContainer<T> : IEnumerable<T>, IEnumerator<T> {
    private T t;
    
    public MyContainer(T t) { this.t = t; }
      
    public IEnumerator<T> GetEnumerator() => this; // 1
    IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); // 2
    
    public T Current => this.Current; // 1
    object IEnumerator.Current => t; // 2
      
    public bool MoveNext() => true;
    public void Reset() {}
    public void Dispose() {}
}

Notice that I implemented Current and GetEnumerator twice each. I could have made them do different things, but I didn't here. I just made the ones marked "2" delegate to the ones marked "1".

If I call GetEnumerator through a variable of type IEnumerable, I would call the implementation marked "2", otherwise I would have called the implementation marked "1",

void Foo(IEnumerable x, IEnumerable<int> y) {
    x.GetEnumerator(); // Implementation "2"
    y.GetEnumerator(); // Implementation "1"
}

and this is what it meant by "hiding",

  •  Tags:  
  • c#
  • Related