В C#, интерфейсы представляют собой особый «вид» типа, который отличается от класса в нескольких ключевых направлениях:
- Вы не можете включать любой код реализации их методов.
- Один класс может «наследовать» (называемый «реализация») столько, сколько он хочет.
- Вы не можете создать экземпляр интерфейса
new
.
(Есть также некоторые особенности языка, связанные с интерфейсами, такие как явные реализации, но это не важно для обсуждения.)
Кроме того, интерфейсы могут использоваться практически везде, где вы можете использовать любой другой ссылочный тип. Это включает определение полей, свойств или локальных переменных с типами интерфейсов или их использование в качестве типов параметров или возвращаемых типов для методов.
Хитрость заключается в том, если вы определяете свойство, как, скажем, IEnumerable<int>
, и вы хотите установить это значение, вы не можете сделать это:
public IEnumerable<int> Numbers { get; set; }
...
this.Numbers = new IEnumerable<int>();
Это ошибка. Вы не можете создать новый экземпляр интерфейса, потому что это всего лишь «шаблон» - нет ничего «позади», чтобы на самом деле что-либо делать. Тем не менее, вы можете сделать это:
public IEnumerable<int> Numbers { get; set; }
...
this.Numbers = new List<int>();
Поскольку List<T>
реализует IEnumerable<T>
, компилятор будет автоматически выполнять преобразование типа, чтобы сделать работу присваивания. Любой конкретный класс, который реализует IEnumerable<>
, может быть присвоен свойству типа IEnumerable<>
, поэтому вы часто видите свойства интерфейса интерфейса. Он позволяет вам изменить базовый тип бетона (возможно, вы хотите изменить List<T>
на ObservableCollection<T>
, но пользователи вашего класса не знают и не заботятся о том, когда вы это делаете.
То же самое касается методов с возвратным типом интерфейса, кроме дополнительный вариант здесь, что C# бросает в качестве бонуса:
public IEnumerable<string> GetName()
{
// this fails.
return new IEnumerable<string>();
// this works.
return new List<String>();
// this also works because magic!~
yield return "hello";
yield return "there";
yield return "!";
}
Этот последний случай является особой формой «синтаксический сахар», что C# обеспечивает, потому что это такое общее требование, как и другие люди упоминали, компилятор специально. ищет yield return
заявления о методах, возвращающих IEnumerable
или IEnumerator
(как общие, так и не общие версии) и делает некоторую здоровенную перекодировку кода.
За кулисами C# создает скрытый класс, который реализует IEnumerable<string>
и реализации его GetEnumerator
способ вернуть IEnumerator<string>
объект, который предоставляет эти три строковые значения. Для вас было бы очень много шаблонов для написания, хотя вы, безусловно, могли бы написать это сами. В предыдущих версиях C# не было yield
, и вам пришлось написать его самостоятельно.
Если вы действительно хотите знать, вы можете найти эквивалент C# here, среди других мест. По сути, он принимает метод, который содержит ваши операторы yield
и создает из него , но превращает его в конечный автомат. Он использует эквивалент меток и операторов goto для возврата в нужное место каждый раз, когда потребитель вызывает MoveNext
в том же экземпляре. Кроме того, как я понимаю, он делает то, что вы на самом деле не можете делать на C# (он перескакивает и выходит из циклов), но это законно в IL-коде, поэтому его реализация более эффективна, чем вы могли бы написать сами.
Но как только вы пройдете секретную причину ключевого слова yield
, вы все равно будете делать то же самое. Вы все еще создаете класс, который реализует IEnumerable<>
, и используя это как возвращаемое значение для вашего метода.
'yield return' is magic. Он возвращает 'IEnumerable' типа после оператора' return'. – Travis
Вот несколько вопросов по теме: http://stackoverflow.com/questions/317462/some-help-understanding-yield http://stackoverflow.com/questions/288808/is-yield-return-ienumerable-ienumerator http : //stackoverflow.com/questions/39476/what-is-the-yield-keyword-used-for-in-c Классы 'Task' /' Task 'также имеют специальную поддержку компилятора, так что' async'/' await' может быть реализована путем создания (скрытого) конечного автомата, который очень похож на тот, который создан для ключевого слова 'yield'. –
spender
@Andrew B: ваш второй пример - свойство с типом интерфейса, полностью отличается от вашего первого. Вы можете использовать интерфейсы как свойства, параметры, типы возвращаемых данных и т. Д., Когда захотите, до тех пор, пока вы переходите к * заданию * свойства или * возвращаете * значение, у вас есть конкретная реализация для работы. 'yield' является особенным, потому что он создает для вас конкретную реализацию. –