Например, предположим, что мне нужен ICarинтерфейс, и что все реализации будут содержать поле Year. Значит ли это, что каждая реализация должна декларировать отдельно Year? Разве не было бы лучше просто определить это в интерфейсе?
Например, предположим, что мне нужен ICarинтерфейс, и что все реализации будут содержать поле Year. Значит ли это, что каждая реализация должна декларировать отдельно Year? Разве не было бы лучше просто определить это в интерфейсе?
Ответы:
Хотя многие другие ответы верны на семантическом уровне, я нахожу интересным также подойти к этим видам вопросов с уровня детализации реализации.
Интерфейс может рассматриваться как набор слотов , которые содержат методы . Когда класс реализует интерфейс, класс должен сообщить среде выполнения, как заполнить все необходимые слоты. Когда ты говоришь
interface IFoo { void M(); }
class Foo : IFoo { public void M() { ... } }
класс говорит: «когда вы создаете мой экземпляр, вставьте ссылку на Foo.M в слот для IFoo.M.
Затем, когда вы делаете звонок:
IFoo ifoo = new Foo();
ifoo.M();
компилятор генерирует код, который говорит: «спросите у объекта, какой метод находится в слоте для IFoo.M, и вызовите этот метод.
Если интерфейс представляет собой набор слотов, которые содержат методы, то некоторые из этих слотов могут также содержать методы получения и установки свойства, методы получения и установки индексатора, а также методы добавления и удаления события. Но поле это не метод . Нет никакого «слота», связанного с полем, которое вы можете затем «заполнить» ссылкой на местоположение поля. И поэтому интерфейсы могут определять методы, свойства, индексаторы и события, но не поля.
An interface cannot contain constants, fields, operatorsиз msdn.microsoft.com/en-us/library/ms173156.aspx )
int One(), то реализация public int One(){return 1;}не является полем.
Интерфейсы в C # предназначены для определения договора, которого будет придерживаться класс, а не конкретной реализации.
В этом духе интерфейсы C # позволяют определять свойства, которые вызывающая сторона должна предоставлять для реализации:
interface ICar
{
int Year { get; set; }
}
Реализующие классы могут использовать авто-свойства для упрощения реализации, если с этим свойством не связана специальная логика:
class Automobile : ICar
{
public int Year { get; set; } // automatically implemented
}
Year?
public int Year => 123;, Однако в этом случае нет смысла иметь установщик, поэтому интерфейс должен быть определен с помощьюint Year { get; }
Эрик Липперт прибил это, я буду использовать другой способ сказать то, что он сказал. Все члены интерфейса являются виртуальными, и все они должны быть переопределены классом, который наследует интерфейс. Вы не пишете явно ключевое слово virtual в объявлении интерфейса и не используете ключевое слово override в классе, они подразумеваются.
Виртуальное ключевое слово реализовано в .NET с методами и так называемой v-таблицей, массивом указателей на методы. Ключевое слово override заполняет слот v-таблицы другим указателем метода, перезаписывая тот, который создается базовым классом. Свойства, события и индексаторы реализованы как скрытые методы. Но поля нет. Поэтому интерфейсы могут не содержать полей.
Почему бы просто не иметь Yearсобственность, которая идеально подходит?
Интерфейсы не содержат полей, потому что поля представляют собой конкретную реализацию представления данных, и предоставление их нарушило бы инкапсуляцию. Таким образом, наличие интерфейса с полем будет эффективно кодировать реализацию, а не интерфейс, что является странным парадоксом для интерфейса!
Например, часть вашей Yearспецификации может потребовать, чтобы для ICarразработчиков было недопустимо разрешать присваивание a, Yearкоторое позже текущего года + 1 или до 1900 года. Нельзя сказать, что если вы выставили Yearполя - гораздо лучше использовать свойства вместо того, чтобы делать работу здесь.
Короткий ответ - да, каждый тип реализации должен будет создать собственную переменную поддержки. Это потому, что интерфейс аналогичен контракту. Все, что он может сделать, это указать конкретные общедоступные фрагменты кода, которые должен сделать доступным тип реализации; он не может содержать сам код.
Рассмотрите этот сценарий, используя то, что вы предлагаете:
public interface InterfaceOne
{
int myBackingVariable;
int MyProperty { get { return myBackingVariable; } }
}
public interface InterfaceTwo
{
int myBackingVariable;
int MyProperty { get { return myBackingVariable; } }
}
public class MyClass : InterfaceOne, InterfaceTwo { }
У нас есть пара проблем здесь:
myBackingVariableбудет MyClassиспользовать?Наиболее распространенный подход заключается в объявлении интерфейса и базового абстрактного класса, который его реализует. Это позволяет вам гибко либо наследовать от абстрактного класса и получать реализацию бесплатно, либо явно реализовывать интерфейс и получать наследование от другого класса. Это работает примерно так:
public interface IMyInterface
{
int MyProperty { get; set; }
}
public abstract class MyInterfaceBase : IMyInterface
{
int myProperty;
public int MyProperty
{
get { return myProperty; }
set { myProperty = value; }
}
}
Другие задали «почему», поэтому я просто добавлю, что ваш интерфейс может определять элемент управления; если вы заверните это в свойство:
public interface IView {
Control Year { get; }
}
public Form : IView {
public Control Year { get { return uxYear; } } //numeric text box or whatever
}
Интерфейсы не содержат никакой реализации.
Много уже сказано, но для простоты вот мое мнение. Предполагается, что интерфейсы имеют контракты методов, которые должны быть реализованы потребителями или классами, а не поля для хранения значений.
Вы можете поспорить, что тогда почему свойства разрешены? Таким образом, простой ответ - свойства определены внутри только как методы.
Для этого у вас может быть базовый класс Car, который реализует поле year, и все другие реализации могут наследовать его.
Интерфейс определяет общедоступные свойства и методы экземпляра. Поля, как правило, являются частными или, как правило, наиболее защищенными, внутренними или защищенными внутренними (термин «поле» обычно не используется ни для каких общедоступных).
Как указано в других ответах, вы можете определить базовый класс и определить защищенное свойство, которое будет доступно всем наследникам.
Одна странность состоит в том, что интерфейс может фактически быть определен как внутренний, но он ограничивает полезность интерфейса, и он обычно используется для определения внутренней функциональности, которая не используется другим внешним кодом.