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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 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 ?

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

 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 } 

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

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

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

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

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

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

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

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

  • C #: изменение цвета строки списка?
  • Entity Framework / Linq to SQL: Skip & Take
  • Внедрить общий тайм-аут C #
  • мультимножество, карта и hash-карта сложности
  • Удалить конечные нули
  • Контроль streamа.
  • Ассоциативные массивы в C
  • Удаление десериализации полиморфных classов json без информации о типе с использованием json.net
  • Элегантно вызывать C ++ из C
  • Маршал C ++ struct array в C #
  • C # WinForms ErrorProvider Control
  • Давайте будем гением компьютера.