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

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

Ответ 1

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

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

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

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

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

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

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

Если интерфейс представляет собой набор слотов, содержащих методы, то некоторые из этих слотов также могут содержать методы get и set свойства, методы get и set индексатора и методы добавления и удаления события, Но поле не является методом. Там нет "слота", связанного с полем, которое вы затем можете "заполнить" ссылкой на местоположение поля. И поэтому интерфейсы могут определять методы, свойства, индексы и события, но не поля.

Ответ 2

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

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

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

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

class Automobile : ICar
{
    public int Year { get; set; } // automatically implemented
}

Ответ 3

Объявить его как свойство:

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

Ответ 4

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

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

Ответ 5

Почему бы просто не иметь свойство Year, что отлично?

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

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

Ответ 6

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

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

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 { }

У нас есть пара проблем:

  • Поскольку все члены интерфейса - по определению - public, наша опорная переменная теперь доступна всем, кто использует интерфейс
  • Какой myBackingVariable будет использовать MyClass?

Наиболее распространенным подходом является объявление интерфейса и абстрактного класса barebones, который его реализует. Это позволяет вам гибко либо наследовать от абстрактного класса, либо бесплатно получать реализацию, либо явно реализовывать интерфейс, либо допускать наследование от другого класса. Он работает примерно так:

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
}

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

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

Ответ 11

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

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

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