Как создать интерфейс в 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# начинаются с заглавной буквы I , например, IComparable, IEnumerable (так называемая венгерская нотация), однако это не обязательное требование, а больше стиль программирования.
Что может определять интерфейс? В целом интерфейсы могут определять следующие сущности:
- Методы
- Свойства
- Индексаторы
- События
- Статические поля и константы (начиная с версии C# 8.0)
Однако интерфейсы не могут определять нестатические переменные. Например, простейший интерфейс, который определяет все эти компоненты:
interface IMovable < // константа const int minSpeed = 0; // минимальная скорость // статическая переменная static int maxSpeed = 60; // максимальная скорость // метод void Move(); // движение // свойство string Name < get; set; >// название delegate void MoveHandler(string message); // определение делегата для события // событие event MoveHandler MoveEvent; // событие движения >
В данном случае определен интерфейс IMovable, который представляет некоторый движущийся объект. Данный интерфейс содержит различные компоненты, которые описывают возможности движущегося объекта. То есть интерфейс описывает некоторый функционал, который должен быть у движущегося объекта.
Методы и свойства интерфейса могут не иметь реализации, в этом они сближаются с абстрактными методами и свойствами абстрактных классов. В данном случае интерфейс определяет метод Move, который будет представлять некоторое передвижение. Он не имеет реализации, не принимает никаких параметров и ничего не возвращает.
То же самое в данном случае касается свойства Name. На первый взгляд оно похоже на автоматическое свойство. Но в реальности это определение свойства в интерфейсе, которое не имеет реализации, а не автосвойство.
Модификаторы доступа
Еще один момент в объявлении интерфейса: если его члены — методы и свойства не имеют модификаторов доступа, то фактически по умолчанию доступ public , так как цель интерфейса — определение функционала для реализации его классом. Это касается также и констант и статических переменных, которые в классах и структурах по умолчанию имееют модификатор private. В интерфейсах же они имеют по умолчанию модификатор public. И например, мы могли бы обратиться к константе minSpeed и переменной maxSpeed интерфейса IMovable:
Console.WriteLine(IMovable.maxSpeed); // 60 Console.WriteLine(IMovable.minSpeed); // 0
Но также, начиная с версии C# 8.0, мы можем явно указывать модификаторы доступа у компонентов интерфейса:
interface IMovable < public const int minSpeed = 0; // минимальная скорость private static int maxSpeed = 60; // максимальная скорость public void Move(); protected internal string Name < get; set; >// название public delegate void MoveHandler(string message); // определение делегата для события public event MoveHandler MoveEvent; // событие движения >
Как и классы, интерфейсы по умолчанию имеют уровень доступа internal , то есть такой интерфейс доступен только в рамках текущего проекта. Но с помощью модификатора public мы можем сделать интерфейс общедоступным:
public interface IMovable
Реализация по умолчанию
Также начиная с версии C# 8.0 интерфейсы поддерживают реализацию методов и свойств по умолчанию. Это значит, что мы можем определить в интерфейсах полноценные методы и свойства, которые имеют реализацию как в обычных классах и структурах. Например, определим реализацию метода Move по умолчанию:
interface IMovable < // реализация метода по умолчанию void Move() < Console.WriteLine("Walking"); >>
С реализацией свойств по умолчанию в интерфейсах дело обстоит несколько сложнее, поскольку мы не можем определять в интерфейсах нестатические переменные, соответственно в свойствах интерфейса мы не можем манипулировать состоянием объекта. Тем не менее реализацию по умолчанию для свойств мы тоже можем определять:
interface IMovable < // реализация метода по умолчанию void Move() =>Console.WriteLine("Walking"); // реализация свойства по умолчанию // свойство только для чтения int MaxSpeed < get < return 0; >> >
Стоит отметить, что если интерфейс имеет приватные методы и свойства (то есть с модификатором private), то они должны иметь реализацию по умолчанию. То же самое относится к статическим методам (не обязательно приватным):
Console.WriteLine(IMovable.MaxSpeed); // 60 IMovable.MaxSpeed = 65; Console.WriteLine(IMovable.MaxSpeed); // 65 double time = IMovable.GetTime(500, 10); Console.WriteLine(time); // 50 interface IMovable < public const int minSpeed = 0; // минимальная скорость private static int maxSpeed = 60; // максимальная скорость // находим время, за которое надо пройти расстояние distance со скоростью speed static double GetTime(double distance, double speed) =>distance / speed; static int MaxSpeed < get =>maxSpeed; set < if (value >0) maxSpeed = value; > > >
Добавление интерфейса
Стоит отметить, что в Visual Studio есть специальный компонент для добавления нового интерфейса в отдельном файле. Для добавления интерфейса в проект можно нажать правой кнопкой мыши на проект и в появившемся контекстном меню выбрать Add -> New Item. и в диалоговом окне добавления нового компонента выбрать пункт Interface :
Хотя мы также может добавить стандартный файл класса или любой другой файл кода C# и в нем определить интерфейс.
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, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.
The .NET documentation is open source. Provide feedback here.
Обратная связь
Отправить и просмотреть отзыв по
Урок #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); >>
