Наприклад, припустимо, що я хочу 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; }
Ерік Ліпперт прибив це, я буду використовувати інший спосіб сказати те, що він сказав. Усі члени інтерфейсу є віртуальними, і всі вони повинні бути замінені класом, який успадковує інтерфейс. Ви не чітко пишете віртуальне ключове слово в декларації інтерфейсу, а також не використовуєте ключове слово override у класі, вони маються на увазі.
Віртуальне ключове слово реалізовано в .NET з методами та так званою v-таблицею, масивом покажчиків методів. Ключове слово override заповнює слот v-table іншим вказівником методу, замінюючи той, створений базовим класом. Властивості, події та індексатори реалізуються як методи під капотом. Але поля - ні. Отже, інтерфейси не можуть містити поля.
Чому б просто не мати Year
властивість, яка цілком чудово?
Інтерфейси не містять полів, оскільки поля представляють конкретну реалізацію подання даних, і їх викриття порушить інкапсуляцію. Таким чином, наявність інтерфейсу з полем ефективно кодує реалізацію замість інтерфейсу, що є цікавим парадоксом для інтерфейсу!
Наприклад, частина вашої Year
специфікації може зажадати , щоб він був інвалідом ICar
виконавцями , щоб віднесення до , 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; }
}
}
Багато вже було сказано, але щоб зробити це просто, ось мій погляд. Інтерфейси призначені для використання методів контрактів, які повинні реалізовуватися споживачами або класами, а не мати полів для зберігання значень.
Ви можете заперечити, що тоді чому дозволені властивості? Тож проста відповідь - властивості внутрішньо визначаються лише як методи.
Для цього ви можете мати базовий клас Car, який реалізує поле року, і всі інші реалізації можуть успадкувати його.
Інтерфейс визначає властивості та методи публічних екземплярів. Поля, як правило, є приватними або найбільш захищеними внутрішніми або захищеними внутрішніми (термін "поле", як правило, не використовується для нічого публічного).
Як зазначено в інших відповідях, ви можете визначити базовий клас та визначити захищене властивість, яке буде доступне всім спадкоємцям.
Одна дивна річ у тому, що інтерфейс насправді може бути визначений як внутрішній, але він обмежує корисність інтерфейсу, і він, як правило, використовується для визначення внутрішньої функціональності, яка не використовується іншим зовнішнім кодом.