Отношение реализации интерфейса C# - это просто отношение «может-сделать»?

Сегодня кто-то сказал мне, что реализация интерфейса в C# - это просто взаимосвязь «Может-Делать», а не «Есть-А». Это противоречит моей давней вере в LSP (принцип замещения Лискова). Я всегда думаю, что всякое наследование должно означать отношения «есть-А».

Итак, реализация интерфейса If - это просто связь «Можно-Сделать». Что, если существует интерфейс «IHuman» и «IEngineer», а один класс «Programmer» наследуется от «IHuman» и «IEngineer»? Конечно, «программист» - это «человек» и «инженер-инженер».

Если это просто отношение «Can-Do», значит ли это, что мы не можем ожидать, что поведение экземпляра «Programmer» может отличаться при рассмотрении как IHuman и как IEngineer?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
9
0
8 060
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Я склонен думать об интерфейсах как о контракте поведения. Такие интерфейсы, как IComparable и IEnumerable, являются классическими примерами.

В приведенном вами примере IHuman и IEngineer на самом деле не являются поведениями.

Ответ принят как подходящий

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

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

@Iain: Рад, что тебе это нравится. Отправьте мне отзыв, когда закончите :)

Jon Skeet 01.11.2008 11:56

Что, если бы у вас был абстрактный класс только с абстрактными методами? Чем это будет отличаться от интерфейса? Я считаю, что нет никакой разницы, если только метод. Тогда почему Microsoft принесла две вещи. Какая реализация интерфейса?

Sudhir.net 20.05.2016 08:08

@ Sudhir.net: Я стараюсь избегать разговоров об отношениях типа «есть-а» и т. д., Как я отметил в первом предложении. Абстрактный класс только с абстрактными методами отличается от интерфейса различными деталями, такими как класс, который может быть производным только от одного другого класса, но может реализовывать несколько интерфейсов. Классы не могут объявлять универсальную дисперсию и всегда являются типами значений (тогда как интерфейсы также могут быть реализованы типами значений). Абстрактные классы легче развить, поскольку новые (не абстрактные) методы могут быть добавлены без нарушения существующих подклассов.

Jon Skeet 20.05.2016 08:47

@Jon, у моего абстрактного класса есть только абстрагирование для всего цикла приложения. В будущем в абтрактном классе нет изменений, например, нет нескольких интерфейсов, нет полей, нет метода без абстрагирования. тогда чем отличается мой интерфейс.? Я хочу понять точку зрения реализации, что думают ребята из Microsoft?

Sudhir.net 20.05.2016 08:53

@ Sudhir.net: Ну, это не похоже на то, чтобы C# был первым языком с таким разделением - для начала, он очень похож на абстрактные классы и интерфейсы в Java. Дело не в том, что ваш абстрактный класс реализует несколько интерфейсов, но если бы у вас был второй абстрактный класс, один класс не мог бы быть производным от обоих ваших абстрактных классов - но он мог реализует несколько интерфейсов. На самом деле, комментарии SO - не лучшее место для изучения различий - я предлагаю вам найти хорошую вводную книгу по C#.

Jon Skeet 20.05.2016 08:57

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

Sudhir.net 20.05.2016 09:06

@ Sudhir.net: Этот человек просто неправ. Когда вы говорите «нет множественного наследования», которое не определяется интерфейсом / абстрактным классом, оно определяется разработчиками / подклассами. Спросите этого человека, как бы он написал структуру, производную от вашего абстрактного класса ... В любом случае, как я уже сказал, это действительно не подходящее место для обсуждения. (Вы можете найти более конкретные вопросы о том, «в чем разница между абстрактным классом и интерфейсом».)

Jon Skeet 20.05.2016 09:10

@Jon: я беру это понимание из этого обсуждения - причина, по которой Micorsoft привносит эти две концепции: Abstract - это суперкласс, также может иметь неабстрактный метод, поля. Интерфейс имеет цель множественного наследования. Это не имеет ничего общего с "is-a" и "can-do"

Sudhir.net 20.05.2016 09:16

@ Sudhir.net: Извините, я несколько раз говорил, что это не место для этого. Я больше не буду здесь комментировать.

Jon Skeet 20.05.2016 09:18

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

Обоснование этого можно найти в разделе Выбор между классами и интерфейсами Руководства разработчика .NET Framework:

An interface defines the signatures for a set of members that implementers must provide. Interfaces cannot provide implementation details for the members.

Итак, поскольку классы-примеры «Программист» и «Инженер», скорее всего, будут иметь свои собственные специфические функции, их было бы более подходящим образом реализовать с использованием наследования.

Собственно, поэтому большая часть интерфейса - это возможности, а не имена, поэтому у вас есть

IComparable, ITestable, IEnumerable

и

Человек, животное, собака и т. д.

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

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

Нашел этот вопрос довольно поздно, но я хотел вмешаться.

Интерфейсы в C# имеют отношение «есть-объект», но не «объект-объект». Скорее это реализация.

Другими словами, для класса Foo, реализующего IBar, следующий тест:

Foo myFoo = new Foo(); 
return myFoo is IBar;

буквально возвращает истину. Вы также можете сказать,

IBar bar = myArrayList[3] as IBar;
Foo foo = bar as Foo;

Или, если для метода требуется IBar, вы можете передать Foo.

void DoSomething(IBar bar) {
}

static void Main() {
    Foo myFoo = new Foo();
    DoSomething(myFoo);
}

Очевидно, что IBar сам по себе не имеет реализации, поэтому отношение «можно сделать» также применимо.

interface IBar { void happy(); }
class Foo : IBar
{
    void happy()
    {
        Console.Write("OH MAN I AM SO HAPPY!");
    }
}
class Program
{
    static void Main()
    {
        IBar myBar = new Foo();
        myBar.happy();
    }
}

Но в этом отношении то же самое верно и для наследования объектов; объект класса Foo, который наследует класс Bar, имеет отношение «можно сделать», а также отношение «есть-есть», точно так же, как и интерфейс. Просто его реализация была заранее построена для этого.

Настоящий вопрос: а-какие, может-сделать-какие? Унаследованный объект класса - это -a-[родительский экземпляр] и can-do-[поведение родителей], тогда как реализация интерфейса-an-[реализация интерфейса] и, следовательно, can-do-[поведение интерфейса].

В большинстве случаев, когда используются программные отношения is-a, такие как перечисленные выше, оценивается только интерфейс, поэтому и унаследованный класс, и реализованный интерфейс имеют одинаковые качества is-a и can-do.

HTH, Джон

Другие вопросы по теме