В интернете много статей на тему интерфейсов, что это такое и как их реализовывать. Но я не нашел внятного ответа кто и зачем их придумал? Я только начинаю изучать C# и вообще не вижу смысла в интерфейсах. Классы удобны например в ситуации когда нам нужно в игре создать много врагов, нам не надо каждому врагу прописовать параметры, можно все задать в классе. Вот пример с метанина:
interface IMovable
Реализация в классе:
// применение интерфейса в классе class Person : IMovable < public void Move() < Console.WriteLine(«Человек идет»); >> // применение интерфейса в структуре struct Car : IMovable < public void Move() < Console.WriteLine(«Машина едет»); >>
Но если я просто удалю интерфейс и сделаю вот так
class Person < public void Move() < Console.WriteLine(«Человек идет»); >>
Код по прежнему будет работать.
Вот и вопрос зачем все это нужно?
Я просто не могу представить себе ситуацию где бы было необходимо использовать интерфейсы.
Уничтожаю C++
В C#8 добавили реализацию метода по умолчанию в интерфейсе тем самым сделав из интерфейса недо-класс.
Смотрю видео на ютубе и там все используют интерфейсы но не говорят зачем, просто как будто так и надо.
Не спроста же все их используют? Но без понимания зачем все это нужно у меня просто не получается найти смысл их применения и понять как работает чужой код когда в нем есть интерфейсы.
Единственный смысл в них вижу в том что не нужно писать документацию к классам, просто наследуем от интерфейса а дальше пусть уже другой программист лезет в код и смотрит чего там и как.
Ни в коем случае не хочу сказать что я тут самый умный а все остальные ошибались, просто хочу разобраться почему придумали интерфейсы если и без них все хорошо работает?
Извините что так сумбурно написал, сам на эмоциях, неделю уже читаю статьи по теме и ни как не могу осилить.
Отслеживать
4,972 1 1 золотой знак 8 8 серебряных знаков 27 27 бронзовых знаков
задан 17 янв 2020 в 23:07
User12351259599491 User12351259599491
242 3 3 серебряных знака 17 17 бронзовых знаков
+1 за желание разобраться.
– user176262
17 янв 2020 в 23:11
Дополню небольшим примером из видео о том как сделать парсер сайта: Человек создает интерфейс с разными полями в частности там есть поле string BaseUrl < get; set; >и в классе он пишет реализацию public string BaseUrl < get; set; >= «https://habrahabr.ru»; У меня просто в этот момент происходит крик в голове «Зачем ты создал этот интерфейс если все равно в классе ты прописал тоже самое. » Это же просто лишний код, который будет путать людей если они не очень хорошо знают c#.
17 янв 2020 в 23:45
На этот вопрос отвечали много раз, используйте поиск. Если вы захотите составить список всех объектов, которые могут двигаться: машин, людей, галактик. какой у него будет тип?
17 янв 2020 в 23:46
см. видео 1 и 2
18 янв 2020 в 0:06
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Что такое сетевой интерфейс? What is a network interface [RU]
Есть как минимум 4 причины для использования интерфейсов. Их может и больше, но я текст пишу из головы, потому остальные причины, если такие есть, загуглите или додумаете сами.
Давайте начнем с того, как строятся большие и сложные системы. Основной механизм построения чего то большого — это разделение этого большого на малые части, определения того, как эти части взаимодействуют, и после этого программирование каждой из частей.
Ключевой момент здесь в том, что вы не можете сразу продумать все эти мелкие части и сазу написать все классы для них.
Давайте возьмем типичный пример. Вы пишете свою игру и вам надо предусмотреть сохранение вашей игры. Вы точно знаете, что именно вам надо сохранить, но вы ещё не знаете, куда вы будете сохранять, в файл или в БД или передавать данные на сервер. Что делать в такой ситуации?
В такой ситуации вам поможет интерфейс. Например, у вас есть класс, который вам надо сохранить.
public class MyGameState < //. my game data >
Напишем для него интерфейс сохранения
public interface IGameStore
Что тут произошло? Вашего сохранения ещё в природе нет, кода для него нет, какой это будет класс, какая у класса-хранилища будет иерария наследования, ничего не известно, но вы уже определили, как ваша игра будет взаимодействовать с этим, ещё не существующим классом. Вы можете этот класс написать позже, или его может написать любой другой программист и все будет работать, так как контракт вашего будушего класса уже известен — вы уже определили с помощью интерфейса все нужные для класса методы.
Другими словами, интерфейс помогает определять взаимодействие между модулями вашей программы, даже если эти модули ещё не существуют.
Но тут вы можете возразить мне и сказать — что вы с таким же успехом можете определить класс для сохранения, например
public absract class GameStore
Да, это верно, вы можете определить абстрактный класс. В таком случае, для реализации сохранения, вам надо будет унаследоваться от абстрактрого класса и реализовать нужные методы. Но давайте подумаем, какие ограничения это накладывает на наш класс сохранения?
А вот какие — наш класс сохранения должен будет быть унаследован именно от указанного абстрактного класса и больше ни от кого. Вы можете возразить — мол, в случае интерфейса будет то же самое — ведь класс сохранения должен его реализовать. Но вот и нет — в C# реализация интерфейса никак не ограничивает класс, так как класс может реализовать сколько угодно интерфейсов. А вот наследование от абстрактного класса — уже дело другое, ведь вы не можете насловаться от 2 классов. Отсюда вторая причина использования интерфйсов — Использование интерфйсов позволяет обойти ограничения множественного наследования.
Окей, ну, допустим, мы решили, что мы будем сохранять нашу игру, но для сохранения нашей игры, нам обязательно надо знать имя текушего игрока — то есть его никнейм. Это требование леко выразить, добавив в наш абстрактный класс конструктор, который принимает имя игрока, например (код может не копилироваться, я его практически на телефоне пишу, но идея должна быть ясна)
public absract class GameStore < protected string PlayerNamepublic GameStore(string playerName) < PlayerName = playerName; >public abstract void SaveGame(MyGameState state); public abstract MyGameState Load(); >
Теперь посмотрите что произошло. Мы не только заставляем классы для сохранения игры наследоваться от нашего абстрактного класса, но ещё и накладываем на них определенные огреничения. Интересны ли эти ограничения остальной части игры? Думаю, нет.
В таком случае, зачем остальной части игры знать об этих огреничениях? Нет никаких причин для этого. Таким образом, Используя интерфейс, вы декларируете желаемое поведение. Используя класс — вы декларируете детали реализации.
То есть, когда у вас был интерфейс в игре, вы декларировали, что «не важно что тут будет за объект, не важно как он будет написан, но мне надо чтобы он мог сохранять и загружать состояние игры» — то есть вам было важно, что объект может делать, при этом не важно, как он это делает. В этом суть инкапсуляции. Когда же вы используете класс, особенно если класс содержит какие либо посторонние детали, вы уже покажываете, что вам важно не только что объект может делать, но и также кем объект является и какие у объекта есть детали реализации.
А теперь давайте посмотрим на это с точки зрения игры. Игре, на самом деле, не интересно знать, как и куда будет происходить сохранение. Игре не интересны детали сохранения. Игре не интересен даже факт — сущестует ли уже написанный класс сохранения. Все, что игре надо — это получить объект, который содержит нужные игре методы. Всё.
При таком подходе связь между игрой и реальным классом сохранения лежит только через интерфейс, что является связью гораздо более слабой. Сам класс может быть написан как угодно, содержать какие угодно огранчения или детали реализации, эти ограничения и детали могут меняться и быть переписаны любое количество раз и все будет работать, пока класс сохранения реализует интерфейс. Таким образом, связь классов через интерфейс является более слабой связью, чем связь классов через абстрактный класс или напрямую, откуда следует выводы:
- Если вы хотите держать свои модули слабо связанными (а вы должны хотеть), используйте для связи интерфейсы
- При использовании интерфейов для связи, на них ложится бремя совместимости — интерфейсы должны меняться как можно реже
- Так как интерфейсы должны меняться как можно реже, написание интерфейса становится задачай более ответственной, чем написание класса. Класс, закрытый интерфейсом, всегда можно переписать. Интерфейс же, используемый во многих компонентах, переписать возможно не всегда, при этом есть большой риск потери обратной совместимости с уже написанным до этого кодом.
Источник: ru.stackoverflow.com
C# интерфейсы для начинающих. Что это такое и зачем их использовать?
Доброго времени суток. Сегодня хочу поговорить об интерфейсах в рамках языка C#. Обсудим, что они из себя представляют и для чего они нужны.
Для кого эта статья?
Данная статья отлично подойдет тем, кто только начинает изучать интерфейсы, а также тем, кто их изучил, но не до конца понимает, для чего их использовать и зачем они нужны. Это нормально, так как когда я изучал интерфейсы, первым вопросом был: «А зачем?» 🙂
Что такое интерфейсы?
Интерфейсы представляют собой ссылочный тип, который может содержать в себе набор свойств и методов без реализации (однако начиная с версии C# 8.0 есть возможность реализовать метод в интерфейсе, но об этом чуть позже). И в дальнейшем какой-либо класс может реализовать данный интерфейс. Звучит страшно? Давайте разбираться.
Давайте рассмотрим интерфейсы на практике. Интерфейсы объявляются с помощью ключевого слова interface. Далее вы пишите название интерфейса. Как правило, к названию интерфейса следует приписывать вначале большую букву I, что означает Interface. Называть его следует так, чтобы название описывало поведение интерфейса, то есть что он делает и вообще для чего он.
Например, если мы хотим объявить интерфейс, который будет содержать метод Say, то неплохим названием будет ISpeakable. Сейчас все покажу.
Давайте для начала создадим данный интерфейс
interface ISpeakable
Как видите, реализацию мы не написали. Теперь давайте создадим класс Person, который будет реализовывать данный интерфейс.
public class Person : ISpeakable < public void Say() < Console.WriteLine(«Hi!»); >>
Здесь мы создали класс Person, написали двоеточие и указали интерфейс, который хотим реализовать. При реализации интерфейса мы должны объявить в классе все методы и свойства интерфейса, которого мы реализуем. То есть если в интерфейсе есть метод Say, мы должны его объявить и реализовать.
Теперь мы можем создать объект данного класса и вызвать функцию Say. В результате у нас на консоль выведется сообщение Hi!
Person p = new Person(); p.Say();
Начиная с версии C# 8.0 мы можем указывать реализацию методов прямо в интерфейсе. И тогда, когда мы будем реализовывать интерфейс, нам не обязательно нужно будет реализовывать этот метод. Это что-то типо реализации по умолчанию.
Зачем нужны интерфейсы?
Тут у вас может возникнуть вполне резонный вопрос: зачем мне создавать интерфейс ISpeakable, если я могу объявить функцию Say прямо в классе и без всяких интерфейсов? Давайте разбираться.
Проблема
Допустим у нас есть 2 класса, которые содержат поле Name и метод Move.
public class Person < public string Name < get; set; >public void Move() < Console.WriteLine(«Person is moving. «); >>
public class Animal < public string Name < get; set; >public void Move() < Console.WriteLine(«Animal is moving. «); >>
У них отличается реализация метода Move: в одном движется человек, в другом — животное.
И, допустим, нам нужно создать функцию, которая будет отвечать за движение человека или животного до какой-то точки. Проще говоря, в этой функции человек или животное будут куда-то ходить. В параметры мы хотим передавать наш объект и он будет куда-то идти. Например:
void PersonGoToPoint(Person p) < Console.WriteLine($»go to point»); >
Но есть проблема. Нам придется создавать отдельный метод под каждый класс. Нам придется создать еще и метод под класс Animal.
void AnimalGoToPoint(Animal animal) < Console.WriteLine($»go to point»); >
А если данные классы увеличатся? Если у нас появится, к примеру, еще и класс Fish? Ведь она тоже движется (хоть и плавает) и может дойти доплыть до какой-то точки. Нам придется создать еще один метод.
Это вообще не классно и код у нас будет раздуваться так же, как и живот от кока-колы.
Давайте попробуем сделать иначе. Давайте возьмем и создадим интерфейс IMovable, который будет в себе хранить свойство Name и метод Move
public interface IMoveable < string Name < get; set; >void Move(); >
И теперь пускай наши 2 класса будут реализовывать данный интерфейс
public class Person : IMovable < public string Name < get; set; >public void Move() < Console.WriteLine(«Person is moving. «); >>
public class Animal : IMovable < public string Name < get; set; >public void Move() < Console.WriteLine(«Animal is moving. «); >>
Вроде бы, что изменилось? А теперь смотрите, что можно сделать. Мы можем в параметры функции, которая отвечает за движение до какой-то точки передать не конкретный класс, а наш интерфейс.
void GoToPoint(IMoveable moveable) < Console.WriteLine($»go to point. «); >
То есть мы передали туда наш интерфейс. Что это значит? Теперь при вызове функции мы можем передать туда любой класс, который реализует данный интерфейс. Можно провести аналогию с наследованием: если в параметрах функции указан родительский класс, то мы можем туда передать все его дочерние классы.
IMoveable p = new Person(); p.Name = «John»; p.Move(); GoToPoint(p);
interface (справочник по C#)
Интерфейс определяет контракт. Любой class или struct , реализующий этот контракт, должен предоставлять реализацию для членов, определенных в интерфейсе. Интерфейс может определять реализацию по умолчанию для членов. Он также может определять члены static , чтобы обеспечить единую реализацию для общих функциональных возможностей.
Начиная с C# 11, интерфейс может определять static abstract элементы или static virtual для объявления, что реализующий тип должен предоставлять объявленные члены. Как правило, static virtual методы объявляют, что реализация должна определять набор перегруженных операторов.
В следующем примере класс ImplementationClass должен реализовать метод с именем SampleMethod , не имеющий параметров и возвращающий значение void .
Дополнительные сведения и примеры см. в разделе Интерфейсы.
Пример интерфейса
interface ISampleInterface < void SampleMethod(); >class ImplementationClass : ISampleInterface < // Explicit interface member implementation: void ISampleInterface.SampleMethod() < // Method implementation. >static void Main() < // Declare an interface instance. ISampleInterface obj = new ImplementationClass(); // Call the member. obj.SampleMethod(); >>
Интерфейс может быть членом пространства имен или класса. Объявление интерфейса может содержать объявления (сигнатуры без реализации) следующих членов.
Члены интерфейса по умолчанию
Эти предыдущие объявления элементов обычно не содержат тела. Элемент интерфейса может объявлять текст. Тела членов в интерфейсе являются реализацией по умолчанию. Члены с телом позволяют интерфейсу предоставлять реализацию по умолчанию для классов и структур, которые не предоставляют реализацию с переопределением. Интерфейс может включать:
- Константы
- Операторы
- Статический конструктор
- Вложенные типы
- Статические поля, методы, свойства, индексаторы и события
- Объявления членов с использованием явного синтаксиса реализации интерфейса.
- Явные модификаторы доступа (доступ по умолчанию — public ).
Статические абстрактные и виртуальные члены
Начиная с C# 11 интерфейс может объявлять static abstract элементы и static virtual для всех типов элементов, кроме полей. Интерфейсы могут объявлять, что реализующие типы должны определять операторы или другие статические члены. Эта функция позволяет универсальным алгоритмам указывать поведение, подобное числу.
Примеры можно увидеть в числовых типах в среде выполнения .NET, например System.Numerics.INumber . Эти интерфейсы определяют общие математические операторы, реализованные многими числовыми типами. Компилятор должен разрешать static virtual вызовы методов и static abstract во время компиляции.
Методы static virtual и static abstract , объявленные в интерфейсах, не имеют механизма диспетчеризации среды выполнения, аналогичного virtual методам или abstract , объявленным в классах. Вместо этого компилятор использует сведения о типе, доступные во время компиляции. static virtual Поэтому методы почти исключительно объявляются в универсальных интерфейсах. Кроме того, большинство интерфейсов, объявляющих static virtual методы или static abstract , объявляют, что один из параметров типа должен реализовывать объявленный интерфейс. Например, INumber интерфейс объявляет, что T должен реализовывать INumber . Компилятор использует аргумент типа для разрешения вызовов методов и операторов, объявленных в объявлении интерфейса. Например, int тип реализует INumber . Если параметр T type обозначает аргумент int типа , вызываются члены, static объявленные в int . Кроме того, если double является аргументом типа, вызываются члены, static объявленные в double типе.
Диспетчеризация методов для static abstract методов и static virtual , объявленных в интерфейсах, разрешается с помощью типа времени компиляции выражения. Если тип среды выполнения выражения является производным от другого типа времени компиляции, будут вызваны статические методы базового типа (времени компиляции).
Вы можете попробовать эту функцию, работая с руководством по статическим абстрактным членам в интерфейсах.
Наследование интерфейса
Интерфейсы не могут содержать состояние экземпляра. Статические поля теперь разрешены, но поля экземпляра не разрешены в интерфейсах. Автоматические свойства экземпляра не поддерживаются в интерфейсах, так как они неявно объявляют скрытое поле. Это правило оказывает незначительное воздействие на объявления свойств. В объявлении интерфейса следующий код не объявляет автоматически реализуемое свойство, как в class или struct . Вместо этого он объявляет свойство, которое не имеет реализации по умолчанию, но должно быть реализовано в любом типе, реализующем интерфейс.
public interface INamed < public string Name >
Интерфейс может наследовать от одного или нескольких базовых интерфейсов. Когда интерфейс переопределяет метод, реализованный в базовом интерфейсе, он должен использовать синтаксис явной реализации интерфейса.
Если список базовых типов содержит базовый класс и интерфейсы, базовый класс должен стоять первым в списке.
Класс, реализующий интерфейс, может явно реализовывать члены этого интерфейса. Явно реализованный член может быть доступен не через экземпляр класса, а только через экземпляр интерфейса . Кроме того, обращение к членам интерфейса по умолчанию можно осуществлять только через экземпляр интерфейса.
Дополнительные сведения о явной реализации интерфейса см. в статье Явная реализация интерфейса.
Пример реализации интерфейса
В следующем примере показана реализация интерфейса. В этом примере интерфейс содержит объявление свойства, а класс содержит реализацию. Любой экземпляр класса, который реализует IPoint , имеет целочисленные свойства x и y .
interface IPoint < // Property signatures: int X < get; set; >int Y < get; set; >double Distance < get; >> class Point : IPoint < // Constructor: public Point(int x, int y) < X = x; Y = y; >// Property implementation: public int X < get; set; >public int Y < get; set; >// Property implementation public double Distance => Math.Sqrt(X * X + Y * Y); > class MainClass < static void PrintPoint(IPoint p) < Console.WriteLine(«x=, y=», p.X, p.Y); > static void Main() < IPoint p = new Point(2, 3); Console.Write(«My Point: «); PrintPoint(p); >> // Output: My Point: x=2, y=3
Спецификация языка C#
См. также раздел
- Справочник по C#
- Руководство по программированию на C#
- Ключевые слова в C#
- Ссылочные типы
- Интерфейсы
- Использование свойств
- Использование индексаторов
Источник: learn.microsoft.com