Почему мой открытый класс не может расширить внутренний класс?
Я действительно не понимаю.
Если базовый класс является абстрактным и предназначен только для использования для обеспечения общих функций для общедоступных подклассов, определенных в сборке, почему его нельзя объявлять внутренним?
Я не хочу, чтобы абстрактный класс был видимым для кода вне сборки. Я не хочу, чтобы внешний код знал об этом.
Ответы
Ответ 1
Наследуя от класса, вы предоставляете функциональность базового класса через своего ребенка.
Поскольку дочерний класс имеет более высокую видимость, чем его родительский элемент, вы будете подвергать элементы, которые в противном случае были бы защищены.
Вы не можете нарушить уровень защиты родительского класса, внедряя дочерний элемент с большей видимостью.
Если базовый класс действительно предназначен для использования общедоступными дочерними классами, то вам также необходимо сделать родительскую публикацию.
Другой вариант - сохранить "родительский" внутренний, сделать его не абстрактным и использовать его для составления ваших дочерних классов, а также использовать интерфейс, чтобы заставить классы реализовать функциональные возможности:
public interface ISomething
{
void HelloWorld();
}
internal class OldParent : ISomething
{
public void HelloWorld(){ Console.WriteLine("Hello World!"); }
}
public class OldChild : ISomething
{
OldParent _oldParent = new OldParent();
public void HelloWorld() { _oldParent.HelloWorld(); }
}
Ответ 2
UPDATE: этот вопрос был тема моего блога 13 ноября 2012 года. Посмотрите на это еще несколько соображений по этому вопросу. Спасибо за отличный вопрос!
Вы правы; это не должно быть так. Другие языки OO позволяют "частное наследование", в результате чего тот факт, что D наследуется от B, может быть использован только кодом, который имеет возможность видеть B.
Это было дизайнерское решение оригинальных дизайнеров С#. К сожалению, я сейчас ухожу от своего стола - я провожу пару выходных в течение долгих выходных, поэтому у меня нет указателей на дизайн с 1999 года передо мной. Если я подумаю об этом, когда вернусь, я просмотрю их и посмотрю, есть ли оправдание для этого решения.
Мое личное мнение заключается в том, что наследование должно использоваться для представления "своего рода" отношений; то есть наследование должно представлять семантику модели, моделируемой языком. Я стараюсь избегать ситуаций, когда наследование используется как механизм совместного использования кода. Как отмечали другие, лучше всего предпочесть композицию в наследование, если то, что вы хотите представить, это "этот класс разделяет механизмы реализации с другими классами".
Ответ 3
Я думаю, что самое близкое, что вы можете сделать, - это запретить другим сборкам создание абстрактного класса, сделав его конструктором внутренним, чтобы указать из MSDN
Внутренний конструктор предотвращает использование абстрактного класса в качестве базового класса типов, которые не находятся в той же сборке, что и абстрактный класс.
Затем вы можете попробовать добавить EditorBrowsableAttribute в класс, чтобы попытаться скрыть его от Intellisense (хотя у меня были смешанные результаты используя его, если честно) или поместите базовый класс во вложенное пространство имен, например MyLibrary.Internals
, чтобы отделить его от остальных ваших классов.
Ответ 4
Я думаю, что вы смешаете проблемы здесь, и С# виноват, фактически (и Java перед ним).
Наследование должно служить механизмом категоризации, тогда как часто используется для повторного использования кода.
Для повторного использования кода всегда было известно, что композиция превосходит наследование. Проблема с С# заключается в том, что он дает нам такой простой способ наследования:
class MyClass : MyReusedClass { }
Но для того, чтобы сочинять, мы должны сделать это сами:
class MyClass {
MyReusedClass _reused;
// need to expose all the methods from MyReusedClass and delegate to _reused
}
Что недостает такой конструкции, как trait (pdf), которая привносит композицию в тот же уровень юзабилити, что и наследование.
Здесь вы узнаете о чертах в С# (pdf), и это будет выглядеть примерно так:
class MyClass {
uses { MyTrait; }
}
Хотя я бы хотел увидеть другую модель (роль ролей Perl 6).
UPDATE:
В качестве побочного примечания язык Oxygene имеет функцию, которая позволяет делегировать всех членов интерфейса в свойство-член, которое реализует это интерфейс:
type
MyClass = class(IReusable)
private
property Reused : IReusable := new MyReusedClass(); readonly;
implements public IReusable;
end;
Здесь все элементы интерфейса IReusable
будут отображаться через MyClass
, и все они передадут свойству Reused
. Однако существует проблемы с этим подходом.
ДРУГОЕ ОБНОВЛЕНИЕ:
Я начал внедрять эту концепцию автоматической компоновки в С#: взгляните на NRoles.
Ответ 5
Я думаю, что это нарушит Принцип замены Лискова.
В таких случаях я использовал внутренние классы и предпочитаю состав над наследованием. Есть ли что-либо в вашей конструкции, которая запрещает содержать все такие функции в вашем внутреннем классе, а затем ваши общедоступные классы содержат экземпляр этого внутреннего класса?