В этой статье буду рассмотрены основные моменты при использовании интерфейсов.
Перед прочтением рекомендуется ознакомиться с наследованием классов и преобразованиями типов объекта.
Перед прочтением рекомендуется ознакомиться с наследованием классов и преобразованиями типов объекта.
Интерфейс - контракт ссылочного типа, позволяющий определить некоторый функционал для класса или структуры, при этом не имеющий конкретной реализации.
Для определения интерфейса используется ключевое слово interface, где, как правило, название начинается с заглавной буквы I. Пример IVehicle, IFigure, IEnumerable и так далее...
По умолчанию интерфейсы, как классы или структуры, по уровню доступа являются internal и не могут напрямую в сборке иметь модификаторы доступа с более низким уровнем, таким как protected, protected internal, private, private internal, однако вы можете их сделать public.
Также есть еще несколько важных (для .Net программистов с начальным уровнем опыта) подводных камней:
- члены интерфейса всегда публичные, так как основная задача интерфейса - обязать наследника класса или структуру получить весь его функционал, поэтому в интерфейсах вы не можете задать члену какой-либо модификатор доступа кроме как public
- изменить модификатор доступа самого интерфейса на более низкий уровень вы все же сможете, но только в том случае, если сам интерфейс находится в структуре или классе
Рассмотрим по порядку весь спектр вышеперечисленного:
Пример
namespace Msey
{
public interface IFruit
{
}
interface IBuilding
{
}
internal interface IVehicle // излишне, тк интерфейс уже по умолчанию {internаl}, как объяснялось выше
{
}
private interface ICannot // так нельзя
{
}
class ForPrivateInterfaces
{
private interface ICan // а вот так можно
{
}
}
struct ForPrivateInterfacesToo
{
private interface ICanToo // так тоже можно
{
}
}
}
Пример
Реализация интерфейса в классе выглядит следующим образом:
namespace Msey
{
interface IVehicle
{
void Move();
void Stop();
}
class Car : IVehicle
{
public void Move() // мы должны обязательно реализовать все методы наследуемого интерфейса, иначе будет ошибка компиляции
{
}
public void Stop()
{
}
}
}
Зачастую бывает необходимо наследовать несколько интерфейсов. C# поддерживает множественное наследование интерфейсов, чего, к сожалению (а на самом-то деле к счастью) нельзя сказать в силу наследования классов.
namespace Msey
{
interface IFirst
{
void FirstMove();
}
interface ISecond
{
void SecondMove();
}
class Car : IFirst, ISecond
{
public void FirstMove()
{
}
public void SecondMove()
{
}
}
}
Случай, когда интерфейсы нужно объединить:
public interface A
{
void ActionA();
}
public interface B
{
void ActionB();
}
public interface C
{
void ActionC();
}
interface ABC : A,B,C
{
}
class ImplementationABC : ABC
{
public void ActionA()
{
}
public void ActionB()
{
}
public void ActionC()
{
}
}
Кроме неявного применения интерфейсов рассмотренного выше, также еще существует и его явная реализация. При явной реализации указывается название метода или свойства вместе с названием интерфейса, при этом мы не можем применить какие-либо модификаторы доступа, так как, по умолчанию, члены явно реализованного интерфейса являются приватными и не могут быть доступными из экземпляра класса.
Пример
namespace Msey
{
interface IFirst
{
void FirstMove();
}
interface ISecond
{
void SecondMove();
}
class Car : IFirst, ISecond
{
public void IFirst.FirstMove() // так нельзя, методу задан модификатор доступа
{
}
void ISecond.SecondMove() // а вот так можно; модификаторов доступа нет
{
}
}
}
Иногда бывают такие случаи, когда несколько наследуемых интерфейсов имеют члены с одинаковыми названиями, тогда нам на помощь приходит преобразование типов объекта и явное применение интерфейса:
Пример
namespace Msey
{
interface IFirst
{
void Move();
}
interface ISecond
{
void Move();
}
class Car : IFirst, ISecond
{
void IFirst.Move()
{
}
void ISecond.Move()
{
}
public void BothInvoke()
{
((IFirst)this).Move();
((ISecond)this).Move();
}
}
}
Здесь разберем подробнее: при вызове BothInvoke(), через this берется ссылка на объект, в котором, собственно, и производится данный вызов. В нашем случае будет Car. Далее мы приводим тип объекта Car к интерфейсу IFirst и производим от его имени вызов метода Move().
Другими словами, от имени интерфейса IFirst мы в объекте Car вызываем метод Move().
Аналогично с интерфейсом ISecond.
((ISecond)this).Move();
Для чего нужны интерфейсы?
Это, пожалуй, самый частый вопрос который я слышал от начинающих программистов, и себя в том числе на начальных этапах программирования на C#. (ой, да ладно, я и до сих пор каждое утро себя спрашиваю).
- Интерфейс в практическом смысле дает возможность указать из чего именно должен состоять тот или иной объект разрабатываемой модели без описания поведения объекта.
- Интерфейс позволяет максимально ослабить зависимости между объектами.
- Интерфейс обеспечивает тестируемость за счет предыдущего пункта
Элементарный пример использования интерфейса
class Program
{
public interface IFigure
{
int NumberOfAngles { get; }
}
public class Triangle: IFigure
{
public int NumberOfAngles => 3;
}
public class Quad : IFigure
{
public int NumberOfAngles => 4;
}
public class Circle : IFigure
{
public int NumberOfAngles => 0;
}
static void Main(string[] args)
{
List<IFigure> AllFigures = new List<IFigure>();
Triangle triangle = new Triangle();
Quad quad = new Quad();
Circle circle = new Circle();
AllFigures.Add(triangle);
AllFigures.Add(quad);
AllFigures.Add(circle);
foreach (var figure in AllFigures)
Console.WriteLine("Figure " + figure.GetType().Name + " has "+figure.NumberOfAngles + " angles.");
}
}
Вывод программы будет следующим:
Figure Triangle has 3 angles.
Figure Quad has 4 angles.
Figure Circle has 0 angles. // да-да, без обработчика отсутствия углов для краткости
Есть интерфейс, который является родителем трех классов-фигур. Их объединяет целочисленное значение, несущее информацию о количестве углов в фигуре. Как нам добавить все фигуры в список и вывести только необходимую информацию? Все верно. На помощь приходит интерфейс, который строго описывает поведение каждой фигуры, при этом не позволяя выходить за рамки дозволенного.
Буду чрезвычайно признателен правкам и дополнениям.
Ред. Эргалон
Ред. uranus
да и вообще большинство шаблонов проектирования использует интерфейсы
Ред. Msey
Со временем это понимается
Ред. AsagiriGen
Ред. nvc123
ну а вообще если говорить простым языком (т.е. для полных нубов) то интерфейсы нужны для того чтобы убедится что класс имеет реализацию необходимых методов
Ред. AsagiriGen
Ред. nvc123
но тут судя по вопросу человек не понимает не в чём отличие абстрактного класса от интерфейса а применение полиморфизма
*конкурс