Интерфейсы и с чем их едят

Добавлен , опубликован
Раздел:
C#
В этой статье буду рассмотрены основные моменты при использовании интерфейсов.
Перед прочтением рекомендуется ознакомиться с наследованием классов и преобразованиями типов объекта.
Интерфейс - контракт ссылочного типа, позволяющий определить некоторый функционал для класса или структуры, при этом не имеющий конкретной реализации.
Для определения интерфейса используется ключевое слово 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. // да-да, без обработчика отсутствия углов для краткости
Есть интерфейс, который является родителем трех классов-фигур. Их объединяет целочисленное значение, несущее информацию о количестве углов в фигуре. Как нам добавить все фигуры в список и вывести только необходимую информацию? Все верно. На помощь приходит интерфейс, который строго описывает поведение каждой фигуры, при этом не позволяя выходить за рамки дозволенного.
Буду чрезвычайно признателен правкам и дополнениям.
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
2
9
6 лет назад
2
Хм, а давайте подумаем, почему Майкрософт сделали и интерфейс IComparer, и класс Comparer. Ну да, наверное, чтобы в случае класса его наследовали когда хочется унаследовать именно класс (настроение такое - хочу наследовать классы сегодня!). А в случае интерфейса - когда, увы, уже наследуешь один класс и для второго класса места нет, поэтому есть возможность наследовать интерфейс.
Ведь вся суть использования интерфейсов именно в том, что именно для них поддерживается множественное наследование, да.
6
29
6 лет назад
6
Множественное наследование просто побочный эффект. Интерфейс просто объявляет контракт. Важное свойство интерфейса в том что он не имеет собственного состояния.
0
28
6 лет назад
Отредактирован nvc123
0
Doc, тут люди не понимают зачем нужен полиморфизм а ты про контракты и состояния
ну а вообще если говорить простым языком (т.е. для полных нубов) то интерфейсы нужны для того чтобы убедится что класс имеет реализацию необходимых методов
0
9
6 лет назад
Отредактирован AsagiriGen
0
Да просто человек сказал, что он не понимает смысл интерфейсов, а вы ему давай писать то, для чего и обычный абстрактный класс сгодиться, не затрагивая сути его существования - так он точно не изменит свою точку зрения.
2
29
6 лет назад
2
Ну отсутствие состояния и реализации методов (что и позволяет множественно наследоваться) и отличает интерфейс от абстрактного класса.
2
28
6 лет назад
Отредактирован nvc123
2
GeneralElConsul, интерфейс это класс в котором все методы абстрактные и публичные (и прочие искусственные фичи наподобие множественного наследования)
но тут судя по вопросу человек не понимает не в чём отличие абстрактного класса от интерфейса а применение полиморфизма
1
26
6 лет назад
1
Важное свойство интерфейса в том, что он ограничивает доступ к реализации класса. Классу А, работающему со сложным классом Б, не обязательно знать о всех методах и свойствах класса Б. Однако, если класс Б реализовывает определенный интерфейс, класс А может об этом знать, и вместо того чтобы работать с многочисленными методами класса Б, он может общаться с ним через интерфейс, получив доступ только к тем методам, которые ему важны.
Есть даже простой пример на реальных объектах - корпус вашего системника. Внутри системника куча функционала, разъемов, проводов и т.д., однако сам корпус обеспечивает вам доступ к самым важным элементам - кнопкам управления и выходам для различных девайсов. Представьте себе если бы порт USB находился на самой материнке, и чтобы найти его, вам приходилось бы высматривать его с десятков других разъемов на материнке.
0
9
6 лет назад
0
Да банально позволяет "выдергивать" из класса только тот функционал, который нужен.
2
3
6 лет назад
2
Будут еще статьи про C#?
Этот комментарий удален
1
29
6 лет назад
1
Batnik:
Будут еще статьи про C#?
Да. Я как раз над этим работаю.
Просто щас тут еще и понкурс по картам вк3 и времени мало остается, но статьи будут!
*конкурс
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.