Конструкторы и Наследование - PullRequest
14 голосов
/ 06 марта 2009

Давайте рассмотрим пример на C #

public class Foo
{
    public Foo() { }
    public Foo(int j) { }
}

public class Bar : Foo
{

}

Теперь все открытые члены Foo доступны в Bar, кроме конструктора. Я не могу сделать что-то вроде

Bar bb = new Bar(1);

Почему конструкторы не наследуются?

UPDATE

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

Ответы [ 10 ]

16 голосов
/ 06 марта 2009

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

Обычно приводимый пример этого заключается в том, что во многих языках базовый класс для всех объектов (обычно называемый «Объект») имеет конструктор без параметров. Если бы конструкторы были унаследованы, это означало бы, что все объекты имеют конструктор без параметров, и нет никакого способа сказать: «Я хочу, чтобы люди, создающие экземпляр этого класса, предоставляли параметры X, Y и Z, иначе их код не должен компилироваться. " Для многих классов важно, чтобы определенные параметры были определены для их надлежащей функции, а создание конструкторов без наследования является частью способа, которым авторы классов могут гарантировать, что некоторые параметры всегда определены.

Изменить для ответа на комментарии: Рамеш указывает, что если конструкторы были унаследованы так, как он хотел бы, он всегда мог переопределить конструкторы базового класса, используя конструкторы, объявленные в частном порядке в каждом производном классе. Это, конечно, правда, но там есть материально-техническая проблема с этой стратегией. Это требует, чтобы создатели производных классов должны были внимательно следить за базовыми классами и добавлять приватный конструктор, если они хотят наследовать блоки конструктора базового класса. Это не только большая работа для людей, пишущих производные классы, этот тип неявной зависимости между классами - это именно та вещь, которая может вызвать странное поведение.

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

Квинтин Робинсон приводит некоторые очень полезные ответы на этот вопрос в комментариях, которые определенно стоит прочитать.

8 голосов
/ 06 марта 2009

Они (через цепочку), вы должны были бы связать конструктор в вашем производном объекте. IE:

public class Foo
{
    public Foo() { }
    public Foo(int j) { }
}

public class Bar : Foo
{
    public Bar() : base() { }
    public Bar(int j) : base(j) { }
}

Затем конструкторы в производных объектах будут связывать вызовы с конструкторами в базовых объектах.

В этой статье приведено еще несколько примеров, если вы хотите продолжить чтение.

2 голосов
/ 06 марта 2009

Предположим, конструкторы были наследуемыми. Как бы вы отключили унаследованные конструкторы во многих случаях, если бы они не имели смысла для подкласса?

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

2 голосов
/ 06 марта 2009

Конструктор Foo может знать только, как инициализировать объект Foo, поэтому не имеет смысла, что он также должен знать, как инициализировать любой потенциальный подкласс

public class Bar : Foo
{
public Bar(int i) : base(i) { }
}

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

2 голосов
/ 06 марта 2009

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

public class FooRepository
{
    public FooRepository(IDbConnection connection) { ... }
}

Если бы были доступны все общедоступные конструкторы из базовых классов, то пользователь вашего класса репозитория мог бы использовать конструктор по умолчанию System.Object для создания недопустимого экземпляра вашего класса:

var badRepository = new FooRepository();

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

1 голос
/ 06 марта 2009

Некоторые обсуждения

Основная идея - предоставить как можно больше контроля создателю. И вы можете иметь частные базы. Как вы тогда создали объект?

1 голос
/ 06 марта 2009

Конструкторы не наследуются по причинам проектирования. (Обратите внимание, что это та же самая ситуация в каждом объектно-ориентированном языке, о котором я знаю.) Простой ответ заключается в том, что во многих случаях вам бы не хотелось, чтобы были доступны те же конструкторы, что и базовый класс. См. этот поток SO для более полных объяснений.

0 голосов
/ 06 марта 2009

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

0 голосов
/ 06 марта 2009

Простой ответ - язык не работает таким образом.

Реальный вопрос, который вы задаете, заключается в том, почему он не работает таким образом :-) Ну, это произвольный выбор, и он следует из C ++ и Java (и, возможно, многих других языков, которые влияли на C #).

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

0 голосов
/ 06 марта 2009

Я думаю, что вы можете сделать следующее:

public class Bar : Foo
{
  public Bar (int i)
    : base (i)
  {
  }
}

Может быть, я немного не в себе - но это общая идея.

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