Почему интерфейсы C # не могут содержать поля?

223

Например, предположим, что мне нужен ICarинтерфейс, и что все реализации будут содержать поле Year. Значит ли это, что каждая реализация должна декларировать отдельно Year? Разве не было бы лучше просто определить это в интерфейсе?

deltanovember
источник
21
Интерфейсы не имеют реализации, для этого используйте абстрактный класс со свойством Year
PostMan
6
Чтобы добавить к сказанному здесь, интерфейсы - это контракты, а поле - это деталь реализации, которая определяет слот в памяти машины для помещения значения (скалярного или адресного указателя).
Герцмейстер
5
Но если поле является публичным, это часть контракта, а не только детали реализации, верно?
Алекс

Ответы:

238

Хотя многие другие ответы верны на семантическом уровне, я нахожу интересным также подойти к этим видам вопросов с уровня детализации реализации.

Интерфейс может рассматриваться как набор слотов , которые содержат методы . Когда класс реализует интерфейс, класс должен сообщить среде выполнения, как заполнить все необходимые слоты. Когда ты говоришь

interface IFoo { void M(); } 
class Foo : IFoo { public void M() { ... } }

класс говорит: «когда вы создаете мой экземпляр, вставьте ссылку на Foo.M в слот для IFoo.M.

Затем, когда вы делаете звонок:

IFoo ifoo = new Foo();
ifoo.M();

компилятор генерирует код, который говорит: «спросите у объекта, какой метод находится в слоте для IFoo.M, и вызовите этот метод.

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

Эрик Липперт
источник
26
Одна вещь, которую я иногда пропускаю, это java-подобная способность определять константы уровня интерфейса, которая, вероятно, не требует "слота" для поддержки в языке.
Л.Бушкин
2
Мне нравится объяснение простыми словами. Спасибо. «CLR via C #» и «Essential .net volume 1» предоставляют более подробную информацию.
Сандип, Великобритания,
6
Почему у поля нет слота? и такой же вопрос с операторами? Я помню, как слышал о типизации утки с использованием рефлексии, чтобы увидеть, реализован ли интерфейс, даже если класс не наследовал интерфейс. Почему нельзя использовать отражение (или щель), чтобы подтянуть поле? я все еще пишу свой код, поэтому мне могут не понадобиться / хотеть поля, но я был удивлен, обнаружив, что не могу использовать операторы. Операторы точно такие же, как в моем понимании, за исключением того, что не все могут быть перегружены ( An interface cannot contain constants, fields, operatorsиз msdn.microsoft.com/en-us/library/ms173156.aspx )
@acidzombie: вопрос о том, почему интерфейс не может определять операторы, отличается (хотя может быть связан) с тем, почему он не может содержать поля; Я бы предложил опубликовать еще один вопрос, если вы все еще заинтересованы в этом.
Адам Робинсон
1
@ b1nary.atr0phy почему с полями? Например, если я объявил метод int One(), то реализация public int One(){return 1;}не является полем.
Привет, Ангел,
131

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

В этом духе интерфейсы C # позволяют определять свойства, которые вызывающая сторона должна предоставлять для реализации:

interface ICar
{
    int Year { get; set; }
}

Реализующие классы могут использовать авто-свойства для упрощения реализации, если с этим свойством не связана специальная логика:

class Automobile : ICar
{
    public int Year { get; set; } // automatically implemented
}
LBushkin
источник
5
Разве все, что является публичным, не является частью договора. Если у класса есть public int Year, не говорит ли он о том, что в контракте класса есть поле типа Year, которое должно присутствовать и быть доступным?
Дидье А.
1
Поздно для стороны, но нет, в этом случае это означает, что у контракта есть Год СОБСТВЕННОСТИ, который должен выполнить любой постоянный класс. Свойства на самом деле являются методами get / set, для которых автоматически создается поле поддержки, если не требуется никакой специальной логики. Специальный синтаксис только для более четкой записи.
user3613916
Как я могу определить постоянное значение по умолчанию (например, 123) для этой автоматической реализации Year?
lama12345
1
@ lama12345 Я тоже опаздываю на вечеринку, но начиная с C # 6 (2015, .NET Framework 4.6 и .NET Core) вы можете использовать для этого автоматическое свойство. public int Year => 123;, Однако в этом случае нет смысла иметь установщик, поэтому интерфейс должен быть определен с помощьюint Year { get; }
EriF89
56

Объявите это как собственность:

interface ICar {
   int Year { get; set; }
}
Tarydon
источник
47
Вопрос в том, почему интерфейсы C # не могут содержать поля? Это не относится к этому.
AakashM
14
Я согласен, что этот ответ не отвечает на вопрос ОП, но он решил мою проблему.
Горген
36

Эрик Липперт прибил это, я буду использовать другой способ сказать то, что он сказал. Все члены интерфейса являются виртуальными, и все они должны быть переопределены классом, который наследует интерфейс. Вы не пишете явно ключевое слово virtual в объявлении интерфейса и не используете ключевое слово override в классе, они подразумеваются.

Виртуальное ключевое слово реализовано в .NET с методами и так называемой v-таблицей, массивом указателей на методы. Ключевое слово override заполняет слот v-таблицы другим указателем метода, перезаписывая тот, который создается базовым классом. Свойства, события и индексаторы реализованы как скрытые методы. Но поля нет. Поэтому интерфейсы могут не содержать полей.

Ганс Пассант
источник
Вы предоставили техническое / фактическое имя (v-таблица) для концепции слотов, упомянутых Эриком. Спасибо за подробности, Ганс.
RBT
Какой смысл в v-таблице, если интерфейсы запрещены реализациями по умолчанию? Это меняется в C # 8.0, но это не относится к делу.
AnthonyMonterrosa
19

Почему бы просто не иметь Yearсобственность, которая идеально подходит?

Интерфейсы не содержат полей, потому что поля представляют собой конкретную реализацию представления данных, и предоставление их нарушило бы инкапсуляцию. Таким образом, наличие интерфейса с полем будет эффективно кодировать реализацию, а не интерфейс, что является странным парадоксом для интерфейса!

Например, часть вашей Yearспецификации может потребовать, чтобы для ICarразработчиков было недопустимо разрешать присваивание a, Yearкоторое позже текущего года + 1 или до 1900 года. Нельзя сказать, что если вы выставили Yearполя - гораздо лучше использовать свойства вместо того, чтобы делать работу здесь.

Джон Феминелла
источник
18

Короткий ответ - да, каждый тип реализации должен будет создать собственную переменную поддержки. Это потому, что интерфейс аналогичен контракту. Все, что он может сделать, это указать конкретные общедоступные фрагменты кода, которые должен сделать доступным тип реализации; он не может содержать сам код.

Рассмотрите этот сценарий, используя то, что вы предлагаете:

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; }
    }
}
Адам Робинсон
источник
7

Другие задали «почему», поэтому я просто добавлю, что ваш интерфейс может определять элемент управления; если вы заверните это в свойство:

public interface IView {
    Control Year { get; }
}


public Form : IView {
    public Control Year { get { return uxYear; } } //numeric text box or whatever
}
zomf
источник
3

Интерфейсы не содержат никакой реализации.

  1. Определите интерфейс со свойством.
  2. Далее вы можете реализовать этот интерфейс в любом классе и использовать этот класс в будущем.
  3. При необходимости вы можете определить это свойство как виртуальное в классе, чтобы вы могли изменить его поведение.
Amit
источник
3

Много уже сказано, но для простоты вот мое мнение. Предполагается, что интерфейсы имеют контракты методов, которые должны быть реализованы потребителями или классами, а не поля для хранения значений.

Вы можете поспорить, что тогда почему свойства разрешены? Таким образом, простой ответ - свойства определены внутри только как методы.

Пракаш Трипати
источник
1
Если вам нужен доступ к члену, просто сделайте его собственностью, и вам будет хорошо.
Unome
0

Для этого у вас может быть базовый класс Car, который реализует поле year, и все другие реализации могут наследовать его.


источник
0

Интерфейс определяет общедоступные свойства и методы экземпляра. Поля, как правило, являются частными или, как правило, наиболее защищенными, внутренними или защищенными внутренними (термин «поле» обычно не используется ни для каких общедоступных).

Как указано в других ответах, вы можете определить базовый класс и определить защищенное свойство, которое будет доступно всем наследникам.

Одна странность состоит в том, что интерфейс может фактически быть определен как внутренний, но он ограничивает полезность интерфейса, и он обычно используется для определения внутренней функциональности, которая не используется другим внешним кодом.

Фроде Н. Росанд
источник