Search code examples
c#interfaceienumerableaccess-modifiersinterface-implementation

Why can't I explicitly set the access level for GetEnumerator?


I'm creating a simple collection that is implementing ICollection<T>, which from my understanding inherits from IEnumerable<T> (it is called interface inheritance at that point, correct?). With the implementation's auto generated code, there's a method for GetEnumerator using a syntax that has me a bit puzzled:

IEnumerator IEnumerable.GetEnumerator() { ... }

I initially thought this was a private method as all other auto generated blocks had specified public and I was trying to categorize and cleanup the implementation code. With that, out of habit, I try to always be explicit with my access modifiers and tried to mark the method with all access levels. However, this caused an error, very simple in nature:

The modifier x is not valid for this item.

This led me to question what's actually going on, yet my web searches aren't turning anything up.


Why can't I set the access level for this method explicitly?


Solution

  • Normally when you implement an interface, you must specify public for the methods and properties. In this case, not only do those methods and properties contribute towards implementing the interface, they are also just like any other regular method or property. Being able to add public to them makes sense. However, the IEnumerator.GetEnumerator you've declared here is a bit special.

    This is an explicit interface implementation of IEnumerable.GetEnumerator. Just in case you didn't know, this is the non-generic (by which I mean, returns a non-generic IEnumerator) version of IEnumerable<T>.GetEnumerator, declared in IEnumerable (not to be confused with IEnumerable<T>!). Since IEnumerable<T> inherits from IEnumerable, you are required to implement both when implementing IEnumerable<T>.

    If you didn't use an explicit interface implementation here, you would have two methods with the same name, both accepting no parameters, with only the return value different:

    public IEnumerator<int> GetEnumerator()
    {
        ...
    }
    
    public IEnumerator GetEnumerator() // CS0111
    {
        ...
    }
    

    This is not allowed. This is where explicit interface implementation comes in. If you declare the non-generic GetEnumerator by prefixing the name of the interface where it comes from:

    IEnumerator IEnumerable.GetEnumerator()
    {
        ...
    }
    

    Then this method is only accessible on an expression with a type of IEnumerable. For example, let's say your collection is called MyCollection.

    MyCollection myCollection = ...;
    // this accesses the *generic* GetEnumerator, the non-generic one is not accessible
    myCollection.GetEnumerator() 
    
    // the non generic one is only accessible if you cast to IEnumerable
    ((IEnumerable)myCollection).GetEnumerator()
    

    Even if in the same class, you can't access the non-generic GetEnumerator without casting to IEnumerable first. Do you see how this is somewhat different from what private usually means? None of the access modifiers can really express the accessibility of explicit interface implementations, to be honest.