Почему абстрактный класс и интерфейс существуют в C #? - PullRequest
12 голосов
/ 22 июня 2009

Почему абстрактный класс и интерфейс существуют в C #, если мы можем реализовать функцию интерфейса, сделав все члены класса абстрактными.

Это потому что:

  1. Существует интерфейс с множественным наследованием
  2. Имеет смысл иметь интерфейс, потому что функция CAN-DO объекта должна быть помещена в интерфейс, а не в базовый абстрактный класс.

Пожалуйста, уточните

Ответы [ 11 ]

27 голосов
/ 22 июня 2009

Ну, абстрактный класс может указывать некоторую реализацию, но обычно не все. (Сказав, что, вполне возможно обеспечить абстрактный класс без абстрактных членов, но множество виртуальных, которые с реализациями «без операции»). Интерфейс обеспечивает реализацию no , просто контракт.

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

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

РЕДАКТИРОВАТЬ: Точно так же, как наиболее удобный способ опровергнуть третью мысль Лбушкина в его комментарии ... вы можете переопределить абстрактный метод не виртуальным (в смысле невозможности переопределить его далее), запечатав его:

public abstract class AbstractBase
{
    public abstract void Foo();
}

public class Derived : AbstractBase
{
    public sealed override void Foo() {}
}

Классы, производные от Derived, не могут переопределять Foo.

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

15 голосов
/ 22 июня 2009

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

Вы можете видеть это как это против реализует .
т.е. Account может быть абстрактной базовой учетной записью, потому что вы можете иметь CheckingAccount, SavingsAccount и т. Д. Все, что происходит от абстрактного базового класса Account. Абстрактные базовые классы также могут содержать неабстрактные методы, свойства и поля, как и любой обычный класс. Однако интерфейсы только содержат абстрактные методы и свойства, которые должны быть реализованы.

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

Так что, если бы у меня был класс SourceFile, тогда мой класс мог бы выбрать реализацию ISourceControl, которая говорит: «Я искренне обещаю реализовать методы и свойства, которые требуются для ISourceControl»

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

8 голосов
/ 22 июня 2009

Они оба существуют, потому что они очень разные вещи. Абстрактные классы разрешают реализацию, а интерфейсы - нет. Интерфейс очень удобен, так как позволяет мне что-то сказать о типе, который я создаю (он сериализуем, пригоден в пищу и т. Д.), Но он не позволяет мне определять любую реализацию для членов Я определяю.

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

Примечание: Интересно отметить, что Вэнс Морррисон (из команды CLR) рассуждает о добавлении реализаций метода по умолчанию к интерфейсам в будущей версии CLR. Это сильно размыло бы различие между интерфейсом и абстрактным классом. Подробнее см. это видео .

2 голосов
/ 22 июня 2009

Одна важная причина, по которой существуют оба механизма, потому что c # .NET допускает только одиночное наследование, а не множественное наследование, как C ++. Наследование классов позволяет вам наследовать реализацию только из одного места; все остальное должно быть достигнуто путем реализации интерфейсов.

Например, давайте предположим, что я создаю класс, например, подкласс Car и I, на три подкласса: RearWheelDrive, FrontWheelDrive и AllWheelDrive. Теперь я решаю, что мне нужно нарезать свои классы вдоль другой «оси», например, с кнопочными пусковыми устройствами и без. Я хочу, чтобы на всех автомобилях, запускаемых с помощью кнопки, был метод PushStartButton (), а на автомобилях без кнопки - метод TurnKey (), и я хочу иметь возможность обрабатывать объекты Car (относительно их запуска) независимо от того, какой подкласс они есть. Я могу определить интерфейсы, которые могут реализовывать мои классы, такие как IPushButtonStart и IKeyedIgnition, поэтому у меня есть общий способ иметь дело с моими объектами, которые отличаются способом, который не зависит от одного базового класса, из которого происходит каждый из них.

1 голос
/ 22 июня 2009

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

1 голос
/ 22 июня 2009

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

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

0 голосов
/ 12 ноября 2017

Идея проста - если ваш класс (YourClass) уже является производным от родительского класса (SomeParentClass), и в то же время вы хотите, чтобы ваш класс (YourClass) имел новое поведение, определенное в каком-то абстрактном классе (SomeAbstractClass) ), вы не можете сделать это, просто производные от этого абстрактного класса (SomeAbstractClass), C # не допускает множественного наследования. Однако, если ваше новое поведение было определено в интерфейсе (IYourInterface), вы можете легко получить его из интерфейса (IYourInterface) вместе с родительским классом (SomeParentClass).

Рассмотрим класс Фрукты , полученный двумя детьми ( Яблоко & Банан ), как показано ниже:

class Fruit
{
    public virtual string GetColor()
    {
        return string.Empty;
    }
}

class Apple : Fruit
{
    public override string GetColor()
    {
        return "Red";
    }
}

class Banana : Fruit
{
    public override string GetColor()
    {
        return "Yellow";
    }
}

У нас есть существующий интерфейс ICloneable в C # . Этот интерфейс имеет единственный метод, как показано ниже, класс, который реализует этот интерфейс, гарантирует его клонирование:

public interface ICloneable
{
    object Clone();
}

Теперь, если я хочу сделать мой Apple класс (не Banana класс) клонируемым, я могу просто реализовать ICloneable следующим образом:

 class Apple : Fruit , ICloneable
{
    public object Clone()
    {
        // add your code here
    }

    public override string GetColor()
    {
        return "Red";
    }
}

Теперь рассмотрим ваш аргумент чисто абстрактного класса, если C # имел бы чистый абстрактный класс, скажем Clonable вместо интерфейса IClonable , например:

abstract class Clonable
{
    public abstract object Clone();
}

Не могли бы вы теперь сделать свой класс Apple клонируемым путем наследования абстрактного Клонируемый вместо IClonable ? как это:

// Error: Class 'Apple' cannot have multiple base classes: 'Fruit' & 'Clonable'
class Apple : Fruit, Clonable
{
    public object Clone()
    {
        // add your code here
    }

    public override string GetColor()
    {
        return "Red";
    }
}

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

0 голосов
/ 22 июня 2009

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

Например, интерфейс IEnumerable<T> описывает, что класс может перебирать своих членов, но он также ограничивает доступ к этой единственной способности. List<T> также может получить доступ к элементам по индексу, но когда вы обращаетесь к нему через интерфейс IEnumerable<T>, вы узнаете только о его способности выполнять итерации членов.

Если метод принимает интерфейс IEnumerable<T> в качестве параметра, это означает, что его интересует только возможность перебора элементов. Вы можете использовать несколько различных классов с этой способностью (например, List<T> или массив T[]) без необходимости использования одного метода для каждого класса.

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

0 голосов
/ 22 июня 2009

Они служат двум совершенно различным целям.

Абстрактные классы обеспечивают способ наследования объекта от определенного контракта, а также позволяют определять поведение в базовом классе. Это, с теоретической точки зрения, обеспечивает отношение IS-A в том, что конкретный класс IS-A является специфическим типом базового класса.

Интерфейсы позволяют классам определять (или более одного) контракта, который они будут выполнять. Они допускают использование ACTS-AS или «может использоваться как» тип отношений, в отличие от прямого наследования. Вот почему, как правило, интерфейсы будут использовать прилагательное, так как они называются (IDisposable) вместо существительного.

0 голосов
/ 22 июня 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...