Конструкторы и наследование
Давайте возьмем пример в С#
public class Foo
{
public Foo() { }
public Foo(int j) { }
}
public class Bar : Foo
{
}
Теперь все публичные члены Foo доступны в Bar, кроме конструктора.
Я не могу сделать что-то вроде
Bar bb = new Bar(1);
Почему конструкторы не наследуются?
UPDATE
Я понимаю, что мы можем создавать конструкторы, но я хотел бы знать, почему приведенная выше конструкция неверна. Я уверен, что для этого должна быть веская причина.
Ответы
Ответ 1
Конструкторы не наследуются, потому что это может вызвать странное и непреднамеренное поведение. Более конкретно, если вы добавили новый конструктор в базовый класс, все производные классы получат экземпляр этого конструктора. В некоторых случаях это плохо, потому что, возможно, ваш базовый класс указывает параметры, которые не имеют смысла для ваших производных классов.
Общим примером для этого является то, что на многих языках базовый класс для всех объектов (обычно называемый "Object" ) имеет конструктор без параметров. Если конструкторы были унаследованы, это означало бы, что у всех объектов есть конструктор без параметров, и нет способа сказать "Я хочу, чтобы люди, которые сделали экземпляр этого класса для предоставления параметров X, Y и Z, в противном случае их код не должен компилироваться." Для многих классов важно определить определенные параметры для их правильной функции и сделать конструкторы, не наследуемые, частью того, как авторы классов могут гарантировать, что некоторые параметры всегда определены.
Изменить, чтобы отвечать на комментарии: Рамеш указывает, что если бы конструкторы были унаследованы по своему усмотрению, он всегда мог бы переопределять конструкторы базового класса, используя частные объявленные конструкторы в каждом производном классе. Это, безусловно, так, но там есть логистическая проблема с этой стратегией. Это требует, чтобы писатели производных классов внимательно наблюдали за базовыми классами и добавляли частный конструктор, если они хотят наследовать блок конструктора базового класса. Мало того, что это большая работа для людей, пишущих производные классы, такая неявная зависимость между классами - это именно то, что может вызвать странное поведение.
Рамеш - не то, что вы описали, невозможно было бы добавить к языку. В общем, это не сделано, потому что подобное поведение может запутать людей и привести к большому количеству дополнительной отладки и написанию кода.
Quintin Robinson дает некоторые очень полезные ответы на этот вопрос в комментариях, которые, безусловно, стоит прочитать.
Ответ 2
Они (через цепочку), вам придется привязать конструктор к вашему производному объекту. IE:
public class Foo
{
public Foo() { }
public Foo(int j) { }
}
public class Bar : Foo
{
public Bar() : base() { }
public Bar(int j) : base(j) { }
}
Конструкторы в производных объектах будут затем связывать вызовы с конструкторами в базовых объектах.
В этой статье приводится еще несколько примеров, если вы хотите продолжить чтение.
Ответ 3
Одна из причин, почему вы можете ввести конструктор в класс, состоит в том, что нет смысла иметь экземпляр этого класса без конкретной "зависимости". Например, это может быть класс доступа к данным, который должен иметь подключение к базе данных:
public class FooRepository
{
public FooRepository(IDbConnection connection) { ... }
}
Если все общедоступные конструкторы из базовых классов были доступны, пользователь вашего класса репозитория мог бы использовать конструктор по умолчанию System.Object для создания недопустимого экземпляра вашего класса:
var badRepository = new FooRepository();
Скрытие унаследованных конструкторов по умолчанию означает, что вы можете применять зависимости, не беспокоясь о том, что пользователи создают "недействительные" экземпляры.
Ответ 4
Конструктор Foo
может знать только, как инициализировать объект Foo, поэтому нет смысла, что он также должен знать, как инициализировать любой потенциальный подкласс
public class Bar : Foo
{
public Bar(int i) : base(i) { }
}
История, которую рассказывает конструктор: "Привет, базовый класс, пожалуйста, сделайте все, что вам нужно, чтобы быть в хорошем состоянии, чтобы я мог идти вперед и настраивать себя правильно".
Ответ 5
Предположим, что конструкторы наследуемы. Как бы вы могли отключить унаследованные конструкторы во многих случаях, если они не имеют смысла для подкласса?
Вместо того, чтобы усложнять язык механизмом блокировки наследования, разработчики языка предпочли просто сделать конструкторы не наследуемыми.
Ответ 6
Конструкторы не наследуются по соображениям дизайна. (Обратите внимание, что это та же ситуация на всех объектно-ориентированных языках, о которых я знаю.) Простой ответ заключается в том, что во многих случаях вам действительно не нужны те же конструкторы, что и базовый класс. См. этот поток SO для более полных объяснений.
Ответ 7
Некоторые обсуждения
Основная идея - обеспечить как можно больше контроля над создателем. И у вас могут быть частные базы. Как вы тогда создали объект?
Ответ 8
Я думаю, вы можете сделать следующее:
public class Bar : Foo
{
public Bar (int i)
: base (i)
{
}
}
Возможно, мне немного не нравится, но это общая идея.
Ответ 9
Простой ответ заключается в том, что язык не работает таким образом.
Реальный вопрос, о котором вы просите, - это то, почему он не работает таким образом:-) Ну, это произвольный выбор, и он следует из С++ и Java (и, возможно, многих других langauges, которые влияли на С#).
Вероятная причина заключается в том, что компилятор будет генерировать только конструктор, который не принимает аргументов и просто вызывает родителя, что если вы хотите больше, чем компилятор, сделайте это самостоятельно. Это лучший выбор, так как вероятность того, что вы делаете больше, чем вызов родительского конструктора.
Ответ 10
Действительно, это потому, что родительский конструктор не полностью инициализирует дочерний объект. Конструктор - это нечто вроде личной вещи в этом отношении. Поэтому большинство языков не наследуют конструкторы.