Интерфейс C # не может быть создан, так почему я могу использовать IEnumerable без указания конкретного типа

#c# #.net

#c# #.net

Вопрос:

Я понимаю, что интерфейсы не могут быть созданы, но экземпляр объекта, реализующего интерфейс, может быть создан.

Но в этом примере я работаю с переменной интерфейса, и она никогда не создается для объекта класса.

 public static IEnumerable<Person> GetPeople()
{
    IEnumerable<Person> people = AMethod<IEnumerable<Person>>();
    return people;
}

var people = MyClass.GetPeople();
// ... operations on the people variable
  

Поэтому я ни в коем случае не ссылаюсь на массив, список или другой тип, который реализует IEnumerable. Когда я отлаживаю этот код, отладчик показывает, что переменная people имеет тип IEnumerable< Person>. Но как это может быть, если интерфейсы не являются «реальной вещью», а всего лишь контрактом? Например, в памяти это массив, список, что-то еще? Я читаю курс по интерфейсам, но не могу разобраться в этом.

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

1. Как это AMethod реализовано?

2. «Итак, я ни в коем случае не ссылаюсь на массив, или список, или другой тип, который реализует IEnumerable». — а? Вы читали свой код? «IEnumerable<Персона> люди = aMethod<IEnumerable<Персона>>();» — вот ваша ссылка.

3. Бьюсь об заклад, AMethod создает какой-нибудь экземпляр, который реализует интерфейс.

4. Тогда @BenjaminMaurer DeserializeObject создает конкретный тип. Вы можете посмотреть на результат people.GetType() , чтобы увидеть фактическую реализацию конкретного типа IEnumerable<Person>

5. Да, но этот метод не показан. То, что он повторно создает интерфейс, не означает, что он фактически реализует его внутренне. И в этом вся суть. Скорее всего, он возвращает НЕ «интерфейс», а «экземпляр класса / структуры, который может быть приведен к интерфейсу».

Ответ №1:

 AMethod<IEnumerable<Person>>()
  

Это вызывает метод, который выглядит примерно так:

 public  T AMethod <T> {
      // Do something to create an instance of T
}
  

IEnumerable<Person> это то, что вы передаете в <T> из AMethod подписи. Это универсальный метод.

Результатом этого является создание чего-то конкретного (например, Person[] , List<Person> и т.д.), соответствующего интерфейсу IEnumerable<Person> . Но он создан AMethod , а не какой-либо из опубликованных вами кодов.

Если вам интересно, какой именно тип на самом деле people, и вы не можете просто посмотреть на AMethod , вызов Type concreteType = people.GetType(); подскажет вам.

Ответ №2:

Полезная вещь с такой абстракцией, как IEnumerable<T> , заключается в том, что она реализована многими классами.

Например:

List<T> это IEnumerable<T> .

ImmutableArray<T> Это IEnumerable<T> .

Массив T[] является IEnumerable<T> (хотя это немного магия компилятора, см. Комментарии).

Говоря, что метод возвращает IEnumerable<T> просто означает , что этот метод вернет что-то, что реализует IEnumerable , вам не нужно заботиться о том, какое именно, но вы можете положиться на тот факт, что интерфейс реализован и, следовательно, использовать в нем методы foreach и linq.

Если все, что вам нужно, это иметь возможность перечислять что-либо с помощью foreach или применять методы linq, вы можете скрыть фактическую реализацию (массив, список или что-то еще) и вместо этого работать с IEnumerable .

В общем, почему вы хотите использовать абстракцию? Вот краткий ответ для начала.

Представьте, что вы полагаетесь на конкретную реализацию, например, List<T> , а позже вы передаете это другим функциям в качестве List<T> параметров и т.д… И ваш код растет следующим образом.

Однажды вы понимаете, что по какой-то причине это List<T> не лучший выбор (из-за производительности или потому, что теперь вы используете другую библиотеку, которая возвращает класс другого типа).

Теперь вам нужно распутать все, что основывалось на этом List предположении.

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

1. «T[] — это IEnumerable<T>». На самом деле массив не реализует этот интерфейс, потому что обобщения были введены намного позже, чем массивы. Однако мы получаем некоторую магию компилятора, которая преобразует массив во что-то, что можно повторять.