Подтвердить что ты не робот

Почему мой открытый класс не может расширить внутренний класс?

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

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

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

4b9b3361

Ответ 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

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

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