Почему мой публичный класс не может расширять внутренний класс? - PullRequest
42 голосов
/ 02 сентября 2010

Я действительно не понимаю.

Если базовый класс является абстрактным и предназначен только для предоставления общих функций общедоступным подклассам, определенным в сборке, почему он не должен быть объявлен как внутренний?

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

Ответы [ 5 ]

40 голосов
/ 02 сентября 2010

ОБНОВЛЕНИЕ: Этот вопрос был темой моего блога 13 ноября 2012 года . Посмотрите это для некоторых еще мыслей по этому вопросу. Спасибо за отличный вопрос!


Ты прав; так не должно быть. Другие ОО-языки допускают «частное наследование», при котором тот факт, что D наследует от B, может быть использован только кодом, который способен видеть B.

Это было дизайнерское решение оригинальных дизайнеров C #. К сожалению, я сейчас далеко от своего рабочего стола - я беру пару выходных для длинных выходных - так что передо мной нет заметок по языковым проектам 1999 года. Если я подумаю об этом, когда вернусь, я просмотрю их и посмотрю, есть ли основания для этого решения.

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

38 голосов
/ 02 сентября 2010

Унаследовав от класса, вы раскрываете функциональность базового класса через своего дочернего элемента.

Поскольку дочерний класс имеет более высокую видимость, чем его родительский класс, вы будете открывать элементы, которые в противном случае были бы защищены.

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

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

Другой вариант - сохранить свой «родительский» внутренний объект, сделать его неабстрактным и использовать его для составления дочерних классов, а также использовать интерфейс для принудительного применения классов для реализациифункциональность:

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(); }
}
14 голосов
/ 02 сентября 2010

Я думаю, что самое близкое, что вы можете сделать, - это запретить другим сборкам создавать абстрактный класс, сделав его конструктором внутренним, заключив в кавычки MSDN :

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

Затем вы можете попробовать добавить EditorBrowsableAttribute в класс, чтобы попытаться скрыть его от Intellisense (хотя у меня были смешанные результаты, используя его, если честно) или поместить базовый класс во вложенное пространство имен например, MyLibrary.Internals, чтобы отделить его от остальных ваших классов.

4 голосов
/ 08 сентября 2010

Я думаю, что вы смешиваете здесь проблемы, и на самом деле виноват C # (и Java до этого).

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

Для повторного использования кода всегда было известно, что композиция превосходит наследование.Проблема с C # заключается в том, что он дает нам такой простой способ наследования:

class MyClass : MyReusedClass { }

Но для того, чтобы сочинять, нам нужно сделать это самостоятельно:

class MyClass {
  MyReusedClass _reused;
  // need to expose all the methods from MyReusedClass and delegate to _reused
}

Чего не хватаетэто конструкция, похожая на черту (pdf) , которая приведет композицию к тому же уровню юзабилити, что и наследование.

В C # есть исследование черт (pdf) ,и это будет выглядеть примерно так:

class MyClass {
  uses { MyTrait; }
}

Хотя я хотел бы увидеть другую модель (модель Perl 6).

ОБНОВЛЕНИЕ:

В качестве примечания к языку Oxygene имеется функция , которая позволяет делегировать все элементы интерфейса в свойство члена, реализующее этот интерфейс:

type
  MyClass = class(IReusable)
  private
    property Reused : IReusable := new MyReusedClass(); readonly;
      implements public IReusable;
  end;

Здесь все члены интерфейса IReusable будут доступны через MyClass, и все они будут делегированы свойству Reused.Однако с этим подходом есть проблем .

ДРУГОЕ ОБНОВЛЕНИЕ:

Я начал реализовывать эту концепцию автоматической композиции в C #: takeвзгляд на NRoles .

3 голосов
/ 02 сентября 2010

Я думаю, что это нарушило бы Принцип замещения Лискова .

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

...