У тех, кто только начинает осваивать 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)
{
      var value = anyClass.GetForecast(10);    
}


Как видно, переменная 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)
{
      summ += forecast.Size;
}


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

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
{
int GetForecast(int value);
}


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

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 {Size = 10};

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


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

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

var third = new Third {Size = 10};

 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, но иметь в виду разные интерфейсы. Привидение можно сделать один раз, а его результат потом использовать многократно. Возможно, что в некоторых случаях это сделает код более читаемым.

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

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

Комментарии (76)


  1. lair
    04.12.2017 15:07

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

    Это, заметим, уже неправда. Если у вас потребитель и реализация находятся в разных сборках, то использование интерфейса может позволить упростить управление зависимостями, начиная с того, что потребитель не будет иметь никакой информации о реализации, и заканчивая тем, что потребитель интерфейса может собираться под .net 4, а реализация — под 4.7.


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

    … только в интерфейсе. И это не недостаток, это смысл использования интерфейсов.


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

    Во-первых, иногда вам надо иметь разные типы возвращаемых значений (которые являются частью сигнатуры, но не позволяют делать перегрузку). Самый яркий пример — GetEnumerator.


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


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

    … потому что метод, реализующий метод интерфейса, всегда публичный.


    1. Viktand Автор
      04.12.2017 15:13
      -2

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


      1. lair
        04.12.2017 15:16

        То есть когда вы написали "если какой-то интерфейс реализуется только в одном единственном классе, то [...] он просто не нужен" — вы имели в виду не это, а что-то другое?


        1. Viktand Автор
          04.12.2017 15:23

          Многие задают вопрос типа «Я пишу проект, зачем мне делать в нем интерфейсы?». Я объяснил, что если кроме этого проекта ваш класс нигде больше не существует, то и интерфейс не нужен. Люди удаляют интерфейс и не видят разницы, а значит, полагают они, интерфейс — это что-то избыточное. Вся статья о том, что это не так. Интерфейсы проявляют себя либо в командных проектах, либо при несколько нестандартном взгляде на них. Как в случае с реализацией двух интерфейсов я показал, что можно получить немножко больше того, что изначально было задумано для исключения неоднозначности.


          1. lair
            04.12.2017 15:25

            Я объяснил, что если кроме этого проекта ваш класс нигде больше не существует, то и интерфейс не нужен.

            Так это же неправда.


            Люди удаляют интерфейс и не видят разницы, а значит, полагают они, интерфейс — это что-то избыточное.

            Если люди удалили интерфейс и не увидели разницы, значит в их ситуации интерфейс правда избыточен.


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

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


            1. Viktand Автор
              04.12.2017 15:36
              -2

              Я понял, вы просто из принципа спорите.

              Я объяснил, что если кроме этого проекта ваш класс нигде больше не существует, то и интерфейс не нужен.
              Так это же неправда.

              Что здесь не так? Расскажите, как добавление интерфейса к работающему проекту из пары классов что-то изменит.

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


              1. lair
                04.12.2017 15:47
                +1

                Расскажите, как добавление интерфейса к работающему проекту из пары классов что-то изменит.

                Это от "пары классов" зависит.


                Например: у вас есть сборка Impl, содержащая классы A и B, и есть сборка Client, содержащая класс C, зависящий от класса A. В тот момент, когда вы добавляете IA (A: IA), вы разрываете прямую зависимость, и позволяете разработчику C делать то, что он считает нужным, и так, как он считает нужным, и одновременно упрощаете разработчку A жизнь, уменьшая количество публичных обязательств его класса. Собственно, после этого A можно сделать internal, и вообще не думать, что кто-то на него завяжется.


                А есть ведь и более сложные ситуации: вот у вас есть A и B в одной сборке, и вам надо разбить эту сборку на две для упрощения поддержки. Но вот беда: A и B, которые должны оказаться в разных сборках, зависят друг от друга. Выделение интерфейсов (хотя каждый интерфейс будет реализован ровно одним классом) позволяет решить эту задачу.


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

                А из вашей статьи так и не понятно, зачем они, если и так все работает.


                1. Viktand Автор
                  04.12.2017 15:48

                  Вы привели пример командной разработки, что я и говорил.


                  1. lair
                    04.12.2017 15:55

                    Эээ… нет. Второй пример не имеет никакого отношения к командной разработке.


                    1. Viktand Автор
                      04.12.2017 16:01

                      Но он и к сути вопроса не имеет отношения. Совсем не обязательно, что все вокруг построено на сложных проектах с многочисленными зависимостями. Вот тут ниже заминусили мое предложение добавить интерфейс к HelloWord — это потому что придется согласиться со мной?


                      1. lair
                        04.12.2017 16:07

                        Но он и к сути вопроса не имеет отношения

                        К сути какого вопроса? "Зачем нужны интерфейсы"? Имеет, и прямое.


                        Совсем не обязательно, что все вокруг построено на сложных проектах с многочисленными зависимостями.

                        … так вот интерфейсы нужны как раз тогда, когда у вас сложность проекта переваливает за некий предел (если не брать в расчет те случаи, когда вам интерфейсы навязаны требованиями). Не переваливает — не нужны.


                        Вот тут ниже заминусили мое предложение добавить интерфейс к HelloWord — это потому что придется согласиться со мной?

                        Нет, потому, что это бессмысленный пример.


              1. Szer
                04.12.2017 16:30

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


                1. Viktand Автор
                  04.12.2017 17:04

                  Ну хоть кто-нибудь, прочитайте текст прежде чем что-то отвечать. Где я что предлагал внедрять?


                  1. Szer
                    04.12.2017 17:36

                    Можно пример проектов, где не нужен DI и юнит-тесты? Я про промышленный уровень, а не про личные хеллоуворлды.


          1. aquamakc
            04.12.2017 15:32
            +4

            Я объяснил, что если кроме этого проекта ваш класс нигде больше не существует, то и интерфейс не нужен.

            SOLID, «Слабая связанность», DI, IoC.
            Погуглите эти фразы, чтобы не делать столь громких и, одновременно, неуместных заявлений.


            1. Viktand Автор
              04.12.2017 15:47
              -3

              При чем здесь все это? Добавьте интерфейс к HelloWord'у и покажите всем как лучше будет работать программа, и как я ошибался.


              1. aquamakc
                04.12.2017 16:01

                Мы говорим о хелловорлдах или о программировании, как профессии? Зачем с самого начала в головы начинающих программистов вкладывать ошибочные мысли?
                Я допускаю, что вы неправильно ФОРМУЛИРУЕТЕ эту мысль даже для самого себя. Это должно звучать примерно так:
                Применение интерфейса имеет, как положительный эффект — снижение уровня связанности объектов, что положительно сказывается на возможности расширения функционала разрабатываемого ПО, масштабирования и внесения в него правок. Так и негативные — некоторое увеличение расхода системных ресурсов, увеличение объёма кода и времени на разработку. По этому программист должен определять необходимость применения интерфейса в зависимости от конкретной ситуации.

                Мысль можно развить, дополнить, добавить уточнения, но, согласитесь, это уже намного лучше, чем

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


                1. Viktand Автор
                  04.12.2017 16:11

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


                  1. INC_R
                    04.12.2017 16:18

                    Тогда бы стоило написать, зачем они действительно нужны. То есть SOLID, DI, IOC, полиморфизм и вот это все.


                    А у вас "можно методы с одинаковыми именами сделать, если никак не придумать уникальное". Статья наполовину про то, что "по назначению использовать интерфейсы вам пока не надо, поэтому можно вот такую штуку сделать, потому что можем".


                  1. aquamakc
                    04.12.2017 16:19

                    Прочитайте ещё раз начало моего ответа. Заявлять ненужность интерфейсов даже для однократной реализации — нельзя. ОСОБЕННО в образовательных целях. Какой-нибудь студент это прочитает, примет за чистую монету… за истину в последней инстанции и поимеет большие проблемы в процессе развития карьеры. А перечисли плюсы и минусы интерфейсов, а так-же те фразы, которые я вам предлагал погуглить с самого начала — то и вы минусов бы не нахватали и студенту жизнь бы чуть-чуть облегчили.


                  1. lair
                    04.12.2017 16:26

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


          1. GrimMaple
            04.12.2017 16:18

            Контрпример. В моем движке есть

            interface IFont
            {
                void Draw(...);
            }
            


            Этот интерфейс реализован одним лишь классом
            class SpriteFont : IFont
            {
                public void Draw(...);
                ...
            }
            


            По вашим словам, он тут не нужен. Однако, все другие классы, которые как-то завязаны на шрифтах (например Label) используют не SpriteFont, а IFont. Догадаетесь, почему?

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

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


            1. Viktand Автор
              04.12.2017 19:52

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

              А, все рано же не напишите…


              1. lair
                04.12.2017 21:25

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


                (хотя, заметим, я знаю один пример, зачем, но не думаю, что вы его имеете в виду)


                1. yarosroman
                  05.12.2017 02:15

                  ээээ, dependency injection?


                  1. Rambalac
                    05.12.2017 02:48

                    Для injection класс нужно использовать как минимум при создании контейнера.


                  1. lair
                    05.12.2017 11:16

                    Чтобы при dependency injection класс-реализация нигде не упоминался, нужно (а) автосканирование и (б) либо базовый класс, либо, собственно, интерфейс — который комментарием выше и предлагают не писать.


    1. yarosroman
      05.12.2017 02:08

      Что GetEnumerator яркий пример, я бы не говорил так, учитывая, что тут разработчики применили грязный хак с утинной типизацией.


      1. lair
        05.12.2017 11:15

        Это на стороне потребителя там утиная типизация (да и то не всегда). А вот на стороне реализации IEnumerable<T> честные explicit, потому что иначе не получится.


  1. ilyaplot
    04.12.2017 17:10

    У меня были неоднократные попытки использования интерфейсов, когда я не понимал их истинное предназначение, да и больших проектов в составе нескольких разработчиков я не писал. И пришел к тем же выводам, что и автор поста.
    Но сейчас я совершенно не согласен, потому что начал использовать интерфейсы правильно, мои библиотеки начали использовать другие разработчики, которые наверняка попытаются вернуть int там, где ожидается string, например. Так что, советую пост удалить и попытаться еще раз построить код правильно.


    1. Viktand Автор
      04.12.2017 17:56

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


      1. oxidmod
        04.12.2017 18:27

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


        1. Viktand Автор
          04.12.2017 18:34

          Т.е. вы не допускаете, что интерфейс может быть не нужен? Вот если погуглить по «c# Is there always an interface», то окажется, что там, на западе, где и придумали C# вовсе не считают, что интерфейсы надо везде «сувать». А только там и тогда, когда это нужно. Что логично, в общем-то. Я ни разу не сказал, что интерфейсы лишние в любом проекте, но все словно оглохли. Может у вас секта какая-то свидетелей интерфейса? :)


          1. oxidmod
            04.12.2017 18:46

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

            Ясен красен, что интерфейс не нужен для DTO\VO\Entity классов.
            Интерфейсы нужны на границах слоев. В тех местах где ваше приложение взаимодействует с БД, файловой системой, сторонними сервисами. В тех местах, где возможны множественные имплементации.
            Они позволяют вам увидеть как ваше приложение будет работать до того, как вы заимплементите все эти вещи.


            1. Viktand Автор
              04.12.2017 18:50

              Где тут противоречие с моей статьей? Я пишу, что если все работает без интерфейсов (понимаете, уже работает, все, задача исполнена), то добавив интерфейс вы ни чего не измените и не улучшите. Фантазии на тему «а вдруг потом понадобится» ничего не меняют. Это частный случай, которого может и не быть.


              1. INC_R
                04.12.2017 18:58

                Вот это все работает без интерфейсов
                public class A {
                    public void Run() => Console.WriteLine("A");
                }
                
                public class B {
                    public void Run() => Console.WriteLine("B");
                }
                
                public class C {
                    public void Run() => Console.WriteLine("C");
                }
                
                object GetSmth() => new B();
                
                void Main()
                {
                    var smth = GetSmth();
                
                    if (smth is A a)
                        a.Run();
                
                    if (smth is B b)
                        b.Run();
                
                    if (smth is C c)
                        c.Run();
                }


                1. Viktand Автор
                  04.12.2017 19:06
                  -1

                  По моему нет. Но вам сейчас расскажут про поддержку кода, работу в команде, библиотеки и прочие интересные, но совершенно не уместные штуки.

                  Хотя интерфейс здесь конечно просто просится.


                  1. INC_R
                    04.12.2017 20:01

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


                    Вы уж определитесь, "интерфейс просится" или "не изменится и лучше не станет"?


                    1. Viktand Автор
                      04.12.2017 20:38

                      Что тут не понятно? Три класса имеют одинаковые методы. Было бы красиво прикрутить интерфейс, который они реализую. Но в данном случае ничего кроме красивости это не даст. Все как работало, так и будет работать. Я не знаю какой там контекст, но если это законченный проект (путь такой странный), то интерфейсы ему ни чем не помогут.

                      что касается ревью, то пожалуй:

                       interface Ix {
                              void Run();
                          }
                      
                      public class A: Ix
                          {
                              public void Run() => Console.WriteLine("A");
                          }
                      
                          public class B: Ix
                          {
                              public void Run() => Console.WriteLine("B");
                          }
                      
                          public class C: Ix
                          {
                              public void Run() => Console.WriteLine("C");
                          }
                      
                          class Prg
                          { 
                              object GetSmth() => new B();
                      
                              public  Prg()
                              {
                                  var smth = (Ix)GetSmth();
                                  smth.Run();
                                  Console.ReadKey();
                              }
                          }
                      


                      Это по сути то, что я написал сразу после дополнения, если вы читали (многие по-моему не читали дальше ката)


                      1. INC_R
                        04.12.2017 20:48

                        Что тут не понятно? Три класса имеют одинаковые методы. Было бы красиво прикрутить интерфейс, который они реализую.

                        Да, пожалуй, мне стоило назвать классы чуть иначе. Например, FooTaskRunner, BarTaskRunner и BazTaskRunner. Было бы чуть очевиднее.
                        Понимаете, было бы не "красиво" прикрутить интерфейс. Это необходимо, потому что ООП и далее по тексту.


                        Ваша логика примерно такая: можно не делать интерфейсы, если можно написать без них. Это примерно тот же уровень логики, как "можно не писать грамотно, мы не в школе". Что угодно можно написать без интерфейсов. Но это не значит, что это нужно делать.


                        1. Viktand Автор
                          04.12.2017 21:27

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

                          Я вот просто открыл гитхаб и набрал в поиске «C#», перешел по первой же ссылке — какая-то игра «JumpAttacker». Довольно объемный проект. Походил по файлам. Нет интерфейсов… Может плохо искал. Пошел по другой ссылке (что-то китайское «GavinYellow»). В некоторых классах были интерфейсы, в некоторых нет. Идем далее «statianzo» — где-есть, где-то нет интерфейса.

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

                          Do define an interface if you need to provide a polymorphic hiercharcy of value types
                          , а если полиморфизм не нужен, то не захламяйте код.


                          1. INC_R
                            04.12.2017 21:54
                            +1

                            Ок, у вас в проектах все без исключения классы имеют интерфейсы потому что ООП? И еще наверно абстрактный класс до кучи?

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


                            Я уже ниже писал, что если погуглить англоязычные форумы, то там пишут, что интерфейсы не надо липить везде подряд вот или вот

                            Вам здесь еще никто и не сказал, что их надо лепить везде подряд.


                            1. Viktand Автор
                              04.12.2017 22:18

                              что если реализация одна или что без интерфейса тоже заработает, то он не нужен — бред.
                              Почему?

                              Вам здесь еще никто и не сказал, что их надо лепить везде подряд.


                              Да все только про это и говорят. Я пишу, что интерфейс нужен вовсе не везде а только там, где он нужен, а мне отвечают, что я не прав. Вы же программист? Утверждение обратное «Интерфейс нужен не везде» — это «Интерфейс нужен везде». И при этом все отмечают, что у них как бы не все классы под интерфейсами, но я все равно не прав.


                              1. lair
                                04.12.2017 22:26

                                Я пишу, что интерфейс нужен вовсе не везде а только там, где он нужен

                                … как определить, где нужен интерфейс?


                                1. Viktand Автор
                                  04.12.2017 22:31

                                  Ну это каждый сам решает исходя из задачи. Если бы было четкое правило, то его бы компилятор проверял и вы бы просто проект не собрали.


                                  1. lair
                                    04.12.2017 22:40

                                    Ну то есть на вопрос "я пишу проект, зачем мне делать в нем интерфейсы?" у вас ответа нет?


                                    1. Viktand Автор
                                      04.12.2017 22:51

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

                                      Если вас просят вскопать огород, вы приходите с лопатой, если забить гвоздь, то с молотком. Вы же не потащите «на объект» все возможные инструменты. Так и здесь.

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


                                      1. lair
                                        04.12.2017 23:31

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

                                        Да. Именно поэтому я не пишу статей про интерфейсы в C#.


                                        Вы уже поняли, что зря тут на меня наехали

                                        Отнюдь. Вы пока не переубедили меня ни в одном пунктов.


                          1. lair
                            04.12.2017 21:58

                            Do define an interface if you need to provide a polymorphic hiercharcy of value types

                            Плохая, плохая цитата. Если ей верить, то интерфейсы надо использовать тогда, когда у вас value types (ни одного примера в вашем посте), а все остальное время надо использовать классы:


                            Do favor defining classes over interfaces.
                            Do use abstract [...] classes instead of interfaces to decouple the contract from implementations.

                            … и только если нужно множественное наследование, тогда:


                            Consider defining interfaces to achieve an effect similar to that of multiple inheritance.

                            К счастью, это не "те, кто придумал C#". И сейчас разрабатывают не так: в современных публичных API намного пропорция "интерфейсы — абстрактные классы" — по крайней мере, по моим наблюдениям — существенно сильнее сместилась в сторону интерфейсов.


              1. oxidmod
                04.12.2017 19:06

                Если задача уже исполнена, то зачем об этом писать статью?


                1. Viktand Автор
                  04.12.2017 19:45

                  Вы меня тролите что-ли?


              1. lair
                04.12.2017 21:27

                Я пишу, что если все работает без интерфейсов (понимаете, уже работает, все, задача исполнена), то добавив интерфейс вы ни чего не измените и не улучшите.

                Это зависит от того, что считать улучшением.


                1. Viktand Автор
                  04.12.2017 21:35
                  -1

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


                  1. lair
                    04.12.2017 21:40

                    Да нет, это вполне нормальный жизненный вопрос. Когда вы делаете рефакторинг, вы (обычно) улучшаете код. Поэтому даже если что-то работает без интерфейсов, но можно отрефакторить, применив интерфейсы, станет лучше (читаемее, например).


                    Реальный вопрос возникает дальше: а нужно ли это улучшение? Потому что если этот проект отдали и забыли, и поддерживать его не надо — то, возможно, возиться с рефакторингом тоже не надо, потому что не окупится. А вот если оно работает, но завтра могут прийти и попросить поправить...


                    Собственно, это все рассуждения, который собственно к интерфейсам отношения не имеют, это общие мысли про рефакторинг. В моем опыте случаев, когда что-то сначала писали "просто чтобы работало", а потом рефакторили на "более правильно", очень и очень много.


                    Я сегодня добавлял интерфейс там, где его не было несколько лет (и все прекрасно работало без него).


      1. kahi4
        04.12.2017 18:32

        Я храбро разрабатываю в одиночку. У меня есть коллекция, по которой я хочу иметь возможность итерировать, однако это, будучи коллекцией, не знаю, каких-то узлов в дереве, а именно — трехмерных объектов на сцене, наследует определенные свойства абстрактного класса, реализующего отрисовку геометрии. Абстрактный класс и его наследники предназначены для вывода, перебор для физического движка. В итоге мне нужно реализовать два интерфейса. Как это сделать? В C# нет множественного наследования.


        Или у меня есть интерфейс Event. На какой черт мне нужен абстрактный класс, в котором не реализован ни один метод? Я просто сделаю интерфейс IEvent (вдобавок некоторые события опять же могут реализовывать несколько интерфейсов).


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


        1. Viktand Автор
          04.12.2017 18:39

          Извините, и что? Я где-то был против этого? И это, поосторожнее, за «На какой черт мне нужен абстрактный класс» здесь могут жестко заминусить те, кто у кого в любимом проекте есть абстрактные классы. Ни кто ж не будет вникать в ваш конкретный случай, как я понял.


        1. retran
          06.12.2017 16:21

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

          en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system


      1. A1essandro
        04.12.2017 18:42

        Ну само рассуждение на тему «это для командной работы, а это для одиночной» — имхо, некорректны. Т.е. если программист, в начале карьеры работает сам по себе, то ему не нужно смотреть как идет разработка в корпоративных проектах? А еще сразу отбрасываем open source проекты? Или когда программист «дорастёт», то ему учиться программировать по новому?

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

        class MyFileLogger
        { 
            public void LogMessage(string message) {}
        }
        


        И всё у него было хорошо, пока ему не потребовалось писать лог в базу. Ему придется переписать или реализацию метода, или все ссылки на MyFileLogger исправить на MyDBLogger. А если он юзает свои наработки, возможно, что логгер в какой-то подключаемой библиотеке. Одни недостатки. То ли дело, если бы наш программист заранее подумал, «а мало ли», и сделал интерфейс:

        interface IMyLogger 
        {
            void LogMessage(string message);
        }
        

        и использовал ссылки этого типа, чтобы отвязаться от реализации.

        Мой пример утрирован, но думаю, он должен дать пищу для размышлений.

        Так же есть такое мнение, с которым лично я полностью согласен: «программировать нужно на уровне интерфейсов, а не на уровне реализации». Интерфейсы дают более чистое представление о структуре и логике проекта.


        1. Viktand Автор
          04.12.2017 18:45

          ЛЮДИ!!! Прочитайте текст. Покажите мне где я написал, что интерфейсы не нужны? Вы все ослепли что-ли? Да я сам могу тысячу примеров привести, где они нужны. Я сам использую их в работе. Но утверждать, что ко всякому классу во всех проектах надо цеплять интерфейс (а вдруг пригодится) — это просто странно.


          1. Szer
            04.12.2017 18:54

            Покажите мне где я написал, что интерфейсы не нужны?

            Показываю:


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

            Вам выше рассказали почему это утверждение не соответствует действительности.


            1. Viktand Автор
              04.12.2017 19:01

              Разве я здесь написал, что интерфейсы не нужны вообще?

              Я уверен, что в вашем проекте, которым сейчас вы занимаетесь (если вы реально программист) найдется не мало классов к которым нет интерфейсов.

              Я выше спросил надо написать интерфейс к HelloWord, но мне не ответили по существу. Да и не ответят. Общий тренд уже задан. Комментариев в поддержку не будет, чтобы минусов не нахватать от вас.


              1. Szer
                04.12.2017 19:05

                Разве я здесь написал, что интерфейсы не нужны вообще?

                Конечно нет. Вы написали что "интерфейсы не нужны к классам с единственной реализацией".


                А я вот не согласен с этим утверждением.
                Есть случаи когда к классу с единственной реализацией нужен интерфейс. Тестируемый код должен быть слабосвязанным.
                Добиться этого проще через интерфейсы, поэтому даже если у Вас есть всего одна (и больше не планируется) реализация сервиса работы с очередью для получения данных, проще выделить интерфейс IQueueDataService чтобы замокать её в юнит-тестах с полпинка. Попутно получаем все прочие плюсы слабой связанности.


                Расскажите где я неправ, очень будет любопытно.


                1. Viktand Автор
                  04.12.2017 19:11

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


              1. INC_R
                04.12.2017 20:40

                Я вам по существу отвечу про hello world здесь, ладно? А то вы много раз про это напоминали. Обрадую: к hello world интерфейс не нужен. Но это несостоятельный пример ровно по той причине, что hello world вы пишете примерно один раз в жизни.


                Да, на каждый класс вешать интерфейс не нужно, это факт. Интерфейсы нужны там, где они могут обеспечить полифморфизм, сокрытие и возможность замены реализации и все такое. Но у вас про это нет ни слова, вы не объясняете, когда они нужны. Из вашей статьи и комментариев следует только то, что "если вы пилите свой небольшой проект, то зачем эти интерфейсы, если можно как-нибудь и без них накодить, чтоб работало". И это совершенно бесполезно и даже вредно. Независимо от масштаба проекта и количества разработчиков код должен быть хорошим и поддерживаемым, в том числе за счет использования интерфейсов там, где это нужно. Но где это нужно, вы так и не сказали.


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


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


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


                1. Viktand Автор
                  04.12.2017 21:02
                  -1

                  А почему вы решили, что не планирую ссылаться на HelloWord? А вдруг потребуется поддержка? Все примеры здесь «притянуты за уши» ни чем не лучше друг-друга.

                  Ни кто не хочет смириться с мыслью, что бывают простые проекты.

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

                  По поводу терминологии. Один раз случайно «унаследовал» интерфейс и все. Если верить МСДНу, то создают экземпляр класса, а не «переменную типа». Да, не объявляют. 1:1

                  Прочее читаю придиками не по существу.


                  1. lair
                    04.12.2017 21:31
                    +1

                    Ни кто не хочет смириться с мыслью, что бывают простые проекты.

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


                    1. Viktand Автор
                      04.12.2017 21:45

                      Все 100% классов в ваших проектах имели интерфейсы? Учебные и тестовые проекты не являются программами по определению и обсуждению не подлежат? Это серьезно?

                      По ссылке из вашей единственной статьи (да, я любопытный) перешел на гитхаб в «unitycontainer», походил по классам. Интерфейсы есть далеко не везде. Может уже пора признать, что если интерфейс не нужен, то он и не нужен?


                      1. lair
                        04.12.2017 21:54

                        Может уже пора признать, что если интерфейс не нужен, то он и не нужен?

                        А я, вроде бы, пять часов назад написал вам: "интерфейсы нужны как раз тогда, когда у вас сложность проекта переваливает за некий предел [...] не переваливает — не нужны"


                        Учебные и тестовые программы за этот предел не переваливают. Простая, казалось бы, консольная программка для заливки/выгрузки схем с удаленного сайта — переваливает.


                        По ссылке из вашей единственной статьи (да, я любопытный) перешел на гитхаб в «unitycontainer», походил по классам. Интерфейсы есть далеко не везде.

                        … при этом весь смысл Automapper.Unity (а совсем не UnityContainer, за который я никак не отвечаю) ровно в том, чтобы сделать удобным взаимодействие с интерфейсом (вместо статического класса). И, что характерно, у этого интерфейса всего одна представляющая интерес реализация. Так что вы выбрали неудачный пример (ну или удачный, смотря какую точку зрения доказывать).


                      1. A1essandro
                        04.12.2017 23:37

                        Все 100% классов в ваших проектах имели интерфейсы?

                        Если взять MVC, например, то, скажем, модели (те классы, которые просто представляют сущности) — могут не использовать интерфейсы. Хотя я в моделях реализовывал интерфейсы, типа IDeletable, ISorted и подобные.
                        Вы лично можете не использовать интерфейсы в контроллерах, хотя опять же, зависит от ситуации, да и тот же ApiController реализует IDisposable.
                        Так же в C# существуют статические классы (хелперы, либо классы с расширяющими методами).
                        А еще вложенные типы, которые тоже, на моей практике, часто не реализуют интерфейсы.
                        А еще, у меня сейчас открыт проект, в котором классы Program и Startup не реализуют интерфейсы.
                        Это просто примеры навскидку. Уверен, найдется еще множество причин не использовать интерфейсы

                        И, кстати, Вам не сказал lair, что «все классы должны реализовывать интерфейсы».


                  1. INC_R
                    04.12.2017 21:42
                    +1

                    Если верить МСДНу, то создают экземпляр класса, а не «переменную типа». Да, не объявляют. 1:1

                    О, вы счет ведете? Круто. Так вот:
                    Экземпляр класса создают. Переменную типа объявляют. Это разные вещи.
                    А у вас в статье "объявляют экземпляр класса":


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

                    IForecast any;


                    1. Viktand Автор
                      04.12.2017 22:07

                      Э…
                      Открываем microsoft. Членами класса являются 1,2,3,… Переменных нет, но есть поля класса, которые содержат переменные класса. Идем дальше на страницу полей

                      Поля объявляются в блоке класса путем указания уровня доступа поля, за которым следует тип поля, а затем имя поля.

                      Т.е. в данном случае все таки объявление, но конечно не класса, но и не переменной, а поля. Под которым в дальнейшем подразумевается класс.

                      Я не понимаю, зачем это все здесь обсуждать. Очевидно у вас тоже не уверенные знания.


                      1. INC_R
                        04.12.2017 22:25
                        +1

                        Переменные есть в другой статье, про локальные переменные.


                        У вас IForecast any; — это что? Локальная переменная или поле? Приведите полный кусок кода, без пропусков.
                        Из текста следует, что это локальная переменная. Если вы подразумевали, что это поле, то ладно, суди это не меняет: объявляется поле класса, создается экземпляр класса.


                        И не увлекайтесь сильно русским мсдн-ом, там часто машинный перевод, и он откровенно кривой.


                        но и не переменной, а поля. Под которым в дальнейшем подразумевается класс.

                        Простите, что? Под полем подразумевается класс?


                        Я не понимаю, зачем это все здесь обсуждать. Очевидно у вас тоже не уверенные знания.
                        Затем, что ваша обучающая статья сдержит ошибки и обучаться кому-то по ней противопоказано.


                      1. lair
                        04.12.2017 22:29
                        +1

                        Т.е. в данном случае все таки объявление, но конечно не класса, но и не переменной, а поля. Под которым в дальнейшем подразумевается класс.

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


              1. lair
                04.12.2017 21:28

                Разве я здесь написал, что интерфейсы не нужны вообще?

                Нет, вы написали, что интерфейсы не нужны в конкретном частном случае, и это утверждение неверно.


      1. lair
        04.12.2017 18:43

        Устал повторять, но что вы все зациклились на командной работе?

        Потому что ориентироваться надо именно на нее. И, что занятно, практики, позволяющие одному человеку (без феноменальной памяти) работать с большим и сложным проектом, — те же самые, как если бы там работала команда.


  1. yarosroman
    05.12.2017 02:32

    Эээээ, а по вашему честно, что 95% вашего текста, есть в любой книге по шарпу? И остальные 5% вывод, работает, ну и нефиг трогать.