Как создать интерфейс в c
Перейти к содержимому

Как создать интерфейс в c

  • автор:

Интерфейсы в C#

У тех, кто только начинает осваивать C# часто возникает вопрос что такое интерфейс и зачем он нужен.

Сначала о том, что можно найти по первой же ссылке в поисковике. В большинстве статей смысл интерфейса разъясняется как «договор» о том, что должен содержать класс, какие свойства и методы. Например у нас есть интерфейс:

public interface IForecast < int GetForecast(int value); int Size < get; set; >> 

Соответственно, если какой-то класс реализует данный интерфейс, то он должен содержать реализацию метода int GetForecast(int value) и свойство Size.

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

Подводя итог этой части, если какой-то интерфейс реализуется только в одном единственном классе, то не тратьте время на него. Он просто не нужен.

Дополнение
Сразу после публикации на меня обрушили море критики, сначала я пытался отвечать, но потом понял, что смысла нет. Все критики упирают на то, что я написал в предыдущем абзаце и пытаются приводить сложные примеры проектов где много классов, всякие ссылки туда-сюда, поддержка командой и прочее. Я наверно был не прав, что так коротко подвел итог вступления. Но суть в том, что интерфейс нужен далеко не всегда. Если у вас ваш личный (или просто не очень большой) проект, который не планируется масштабировать, на который ни кто не ссылается и, самое главное, который успешно работает, то введение в проект интерфейсов ничего не изменит. И не надо придумывать истории, что где-то кто-то однажды реализовал интерфейс и обрел бессмертие. Если бы интерфейс был нужен везде, он был бы неотъемлемой частью класса.

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

Конец дополнения

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

Например, предположим у нас есть два класса:

class First
class Second

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

class First : IForecast
class Second : IForecast

Теперь мы можем сделать общий метод для них:

void AnyMethod(IForecast anyClass)

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

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

. IForecast any; … А потом: if(. ) any = new First(); else any = new Second();

Можно пойти еще дальше и объявить массив (или list) таких объектов:

var array = new IForecast[2]; array[0] = new First(); array[1] = new Second();

А потом можно например вычислить сумму всех свойств Size:

var summ = 0; foreach (var forecast in array)

У объявления через интерфейс есть один недостаток: вам будут доступны только методы и свойства, объявленные в интерфейсе и самом классе. Если ваш класс унаследован от базового, то для доступа к его методам придется делать приведение типа:

public class BaseClass < public int GetValue(int value) < return value * 2; >> public class Second: BaseClass, IForecast IForecast frc; frc = new Second(); var frcBase = (BaseClass) frc; var result = frcBase.GetValue(45);

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

Например добавим в наш проект еще один интерфейс:

public interface IForecast2

И создадим новый класс, наследующий оба интерфейса:

public class Third: IForecast, IForecast2 < int IForecast.GetForecast(int value) < return value + Size; >public int Size < get; set; >int IForecast2.GetForecast(int value) < return 2 * value + Size; >>

Это называется явной реализацией интерфейса. Теперь можно построить такую конструкцию:

 var third = new Third ; var v1 = ((IForecast) third).GetForecast(100); var v2 = ((IForecast2)third).GetForecast(100); Console.WriteLine(v1+v2);

Интересно, что при реализации таких интерфейсов методам не могут быть назначены модификаторы доступа (“public” или что-то еще), однако они вполне доступны как публичные через приведение типа.

Как видно, это работает, но выглядит слишком громоздко. Чтобы как-то улучшить код, можно написать несколько иначе:

var third = new Third ; var bad = (IForecast) third; var good = (IForecast2) third; var v1 = bad.GetForecast(100); var v2 = good.GetForecast(100); Console.WriteLine(v1+v2)

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

Еще одно применение интерфейса — быстрое изготовление заглушек для функций.
Например, вы в команде строите большой проект и ваш класс зависит от класса, который пишет коллега. Но еще не написал. Мудрый начальник подготовил интерфейсы всех основных классов еще на первом этапе разработки проекта и вы теперь можете не ждать коллегу, а реализовать класс-заглушку для своего класса. Т.е. временный класс не будет содержать логики, а будет только делать вид что работает: принимать вызовы и возвращать адекватные значения. Для этого надо только реализовать тот же интерфейс, что получил ваш коллега.

Работающий проект со всеми примерами из этой статьи можно посмотреть здесь: ссылка

Как создать интерфейс в c

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

IMovable m = new IMovable(); // ! Ошибка, так сделать нельзя interface IMovable

В конечном счете интерфейс предназначен для реализации в классах и структурах. Например, реализуем выше определенный интерфейс IMovable:

// применение интерфейса в классе class Person : IMovable < public void Move() < Console.WriteLine("Человек идет"); >> // применение интерфейса в структуре struct Car : IMovable < public void Move() < Console.WriteLine("Машина едет"); >>

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

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

Применение интерфейса в программе:

Person person = new Person(); Car car = new Car(); DoAction(person); DoAction(car); void DoAction(IMovable movable) => movable.Move(); interface IMovable < void Move(); >class Person : IMovable < public void Move() =>Console.WriteLine("Человек идет"); > struct Car : IMovable < public void Move() =>Console.WriteLine("Машина едет"); >

В данной программе определен метод DoAction() , который в качестве параметра принимает объект интерфейса IMovable. На момент написания кода мы можем не знать, что это будет за объект — какой-то класс или структура. Единственное, в чем мы можем быть уверены, что этот объект обязательно реализует метод Move и мы можем вызвать этот метод.

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

Консольный вывод данной программы:

Человек идет Машина едет

Реализация интерфейсов по умолчанию

Начиная с версии C# 8.0 интерфейсы поддерживают реализацию методов и свойств по умолчанию. Зачем это нужно? Допустим, у нас есть куча классов, которые реализуют некоторый интерфейс. Если мы добавим в этот интерфейс новый метод, то мы будем обязаны реализовать этот метод во всех классах, применяющих данный интерфейс. Иначе подобные классы просто не будут компилироваться. Теперь вместо реализации метода во всех классах нам достаточно определить его реализацию по умолчанию в интерфейсе. Если класс не реализует метод, будет применяться реализация по умолчанию.

IMovable tom = new Person(); Car tesla = new Car(); tom.Move(); // Walking tesla.Move(); // Driving interface IMovable < void Move() =>Console.WriteLine("Walking"); > class Person : IMovable < >class Car : IMovable < public void Move() =>Console.WriteLine("Driving"); >

В данном случае интерфейс IMovable определяет реализацию по умолчанию для метода Move . Класс Person не реализует этот метод, поэтому он применяет реализацию по умолчанию в отличие от класса Car , который определяет свою реализацию для метода Move.

Стоит отметить, что хотя для объекта класса Person мы можем вызвать метод Move — ведь класс Person применяет интерфейс IMovable , тем не менее мы не можем написать так:

Person tom = new Person(); tom.Move(); // Ошибка - метод Move не определен в классе Person

Множественная реализация интерфейсов

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

class myClass: myInterface1, myInterface2, myInterface3, .

Рассмотрим на примере:

Message hello = new Message("Hello World"); hello.Print(); // Hello World interface IMessage < string Text < get; set; >> interface IPrintable < void Print(); >class Message : IMessage, IPrintable < public string Text < get; set; >public Message(string text) => Text = text; public void Print()=> Console.WriteLine(Text); >

В данном случае определены два интерфейса. Интерфейс IMessage определяет свойство Text, которое представляет текст сообщения. А интерфейс IPrintable определяет метод Print.

Класс Message реализует оба интерфейса и затем применяется в программе.

Интерфейсы в преобразованиях типов

Все сказанное в отношении преобразования типов характерно и для интерфейсов. Поскольку класс Message реализует интерфейс IMessage, то переменная типа IMessage может хранить ссылку на объект типа Message:

// Все объекты Message являются объектами IMessage IMessage hello = new Message("Hello METANIT.COM"); Console.WriteLine(hello.Text); // Hello METANIT.COM // Не все объекты IMessage являются объектами Message, необходимо явное приведение // Message someMessage = hello; // ! Ошибка // Интерфейс IMessage не имеет свойства Print, необходимо явное приведение // hello.Print(); // ! Ошибка // если hello представляет класс Message, выполняем преобразование if (hello is Message someMessage) someMessage.Print();

Преобразование от класса к его интерфейсу, как и преобразование от производного типа к базовому, выполняется автоматически. Так как любой объект Message реализует интерфейс IMessage.

Обратное преобразование — от интерфейса к реализующему его классу будет аналогично преобразованию от базового класса к производному. Так как не каждый объект IMessage является объектом Message (ведь интерфейс IMessage могут реализовать и другие классы), то для подобного преобразования необходима операция приведения типов. И если мы хотим обратиться к методам класса Message, которые не определены в интерфейсе IMessage, но являются частью класса Message, то нам надо явным образом выполнить преобразование типов:

if (hello is Message someMessage) someMessage.Print();

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#
  • Ссылочные типы
  • Интерфейсы
  • Использование свойств
  • Использование индексаторов

Совместная работа с нами на GitHub

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

Урок #22 – Создание интерфейсов

Урок #22 – Создание интерфейсов

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

Видеоурок

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

Что такое интерфейс?

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

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

Как создать интерфейс?

Для создания интерфейса используется ключевое слово Interface :

interface ISomeOne

В интерфейсе можно не прописывать модификаторы доступа и по-умолчанию будет проставлен модификатор public.

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

class Person : ISomeOne < string name; float happiness; int age; public Person(string name, float happiness, int age) < this.name = name; this.happiness = happiness; this.age = age; >public void Change (string val) < this.name = val; Console.WriteLine("Now his name is - " + val); >>

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *