модификаторы доступа для IEnumerable и IEnumerable

#c#

#c#

Вопрос:

Я новичок в C #, просто вопрос по IEnumerable и IEnumerable. Я видел некоторый код, подобный этому:

 public class ShoppingCart : IEnumerable<Product>
{
   public IEnumerator<Product> GetEnumerator()
   {
      return Products.GetEnumerator(); 
   }
   IEnumerator IEnumerable.GetEnumerator() //why 'public' is not needed?
   {
      return GetEnumerator();
   }
}
  

Я понимаю, что первый метод заключается в реализации IEnumerator<Product> , а второй метод заключается в реализации, IEnumerator поскольку IEnumerator наследуется от IEnumerator<Product> , но я не понимаю, почему для второго метода не требуется модификатор ‘public’ access? первый метод действительно имеет значение ‘public’.
И поскольку оба интерфейса имеют одинаковую сигнатуру метода, мы можем видеть, что второй метод использует interfacename.methodname в качестве явной реализации интерфейса, но почему первому методу это не нужно? не должно ли это быть:

 public IEnumerator<Product>.GetEnumerator()
{
   ...
}
  

Комментарии:

1. В некотором смысле, это не все, что общедоступно вообще. Единственный способ получить к нему доступ — через этот интерфейс

2. @BradleyDotNET даже если я помещаю ключевое слово public перед вторым методом, компилятор выдает ошибки

Ответ №1:

Все сводится к различиям между неявной и явной реализацией элементов интерфейса.

  • Явно реализованный элемент не появляется в объекте, если объект не приведен к реализованному интерфейсу. Если вы хотите, чтобы кто-то мог вызывать new ShoppingCart().GetEnumerator() , у вас должен быть GetEnumerator метод, который не является явной реализацией. Предположим, что он соблюдает контракт одного из реализованных интерфейсов, он будет неявно реализовывать метод в этом интерфейсе. В этом случае вы должны указать модификатор доступа, потому что основной целью объявления элемента является сделать его доступным из этого конкретного класса. Модификатор должен быть по крайней мере таким же разрешительным, как интерфейс, который он реализует (и он может быть более разрешительным).
  • При явной реализации элемента интерфейса вам не нужно (и фактически вы не можете) указывать видимость (например, public ), потому что видимость уже задана самим интерфейсом. Поскольку объект должен быть приведен к этому интерфейсу, чтобы получить доступ к этому элементу, считается, что элемент не имеет никакой видимости за пределами видимости интерфейса.

Первый метод в вашем коде мог бы быть явно реализован, вот так:

 IEnumerator<Product> IEnumerable<Product>.GetEnumerator()
{
    return Products.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
    return ((IEnumerable<Product>)this).GetEnumerator();
}
  

Но тот, кто создал этот класс, решил реализовать его неявно, чтобы он был доступен без необходимости сначала приводить ShoppingList к IEnumerable<Product> . Обратите внимание, как это упростило реализацию другого метода, потому что они могли просто вызывать, this.GetEnumerator() не прибегая this к первому приведению.

Но поскольку второй метод отличается от первого только типом возвращаемого значения, невозможно реализовать оба метода неявно. В этих случаях обычной практикой является явная реализация не общего IEnumerable интерфейса.

Комментарии:

1. Но если я изменю первый метод, чтобы использовать явную реализацию точно так же, как второй метод, как IEnumerator<Product>.GetEnumerator() , компилятор все равно выдаст ошибку?

2. @amjad: Это только потому, что другой метод пытается вызвать GetEnumerator() этот ShoppingList класс. Если это реализовано явно, оно больше недоступно в этом классе: вы можете получить к нему доступ, только сначала приведя объект. Смотрите мой обновленный ответ.

3. Извините, я все еще запутался в приведении, согласно вашему коду, разве «это» не относится к экземпляру ShoppingList, тогда GetEnumerator определенно доступен, почему его нужно использовать для доступа к этому методу?

4. @amjad: Это потому, что в коде, который я опубликовал, GetEnumerator() метод реализован только явно. Это означает, что его видимость ограничена кодом, который работает с этим объектом, приведенным как IEnumerable<Product> . Больше не существует никакого GetEnumerator() метода, объявленного в самом ShoppingCart классе. Например, nameof(ShoppingCart.GetEnumerator) отлично работает в исходном коде, где GetEnumerator реализован неявно, но это не разрешится, если все реализации будут явными.