#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>». На самом деле массив не реализует этот интерфейс, потому что обобщения были введены намного позже, чем массивы. Однако мы получаем некоторую магию компилятора, которая преобразует массив во что-то, что можно повторять.