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:
- .NET Framework
IEnumerator<T>
- .NET Framework
IEnumerator
- .NET Core
IEnumerator<T>
- .NET Core
IEnumerator
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",