Частные внутренние классы в С# - почему они не используются чаще?
Я относительно новичок в С#, и каждый раз, когда я начинаю работать над проектом С# (я работал только над почти зрелыми проектами на С#), мне интересно, почему нет внутренних классов?
Может быть, я не понимаю их цели. Для меня внутренние классы - по крайней мере, частные внутренние классы - во многом похожи на "внутренние процедуры" в Pascal/Modula-2/Ada: они позволяют разбивать основной класс на более мелкие части, чтобы облегчить понимание.
Пример: вот что видят большую часть времени:
public class ClassA
{
public MethodA()
{
<some code>
myObjectClassB.DoSomething(); // ClassB is only used by ClassA
<some code>
}
}
public class ClassB
{
public DoSomething()
{
}
}
Так как ClassB будет использоваться (хотя бы некоторое время) только классом A, я предполагаю, что этот код будет лучше выражен следующим образом:
public class ClassA
{
public MethodA()
{
<some code>
myObjectClassB.DoSomething(); // Class B is only usable by ClassA
<some code>
}
private class ClassB
{
public DoSomething()
{
}
}
}
Я был бы рад услышать от вас по этому вопросу - я прав?
Ответы
Ответ 1
Вложенные классы (возможно, лучше всего избегать слова "внутренний", поскольку вложенные классы в С# несколько отличаются от внутренних классов в Java) действительно могут быть очень полезными.
Один шаблон, который не упоминался, - это шаблон "лучшего перечисления", который может быть еще более гибким, чем тот, что в Java:
public abstract class MyCleverEnum
{
public static readonly MyCleverEnum First = new FirstCleverEnum();
public static readonly MyCleverEnum Second = new SecondCleverEnum();
// Can only be called by this type *and nested types*
private MyCleverEnum()
{
}
public abstract void SomeMethod();
public abstract void AnotherMethod();
private class FirstCleverEnum : MyCleverEnum
{
public override void SomeMethod()
{
// First-specific behaviour here
}
public override void AnotherMethod()
{
// First-specific behaviour here
}
}
private class SecondCleverEnum : MyCleverEnum
{
public override void SomeMethod()
{
// Second-specific behaviour here
}
public override void AnotherMethod()
{
// Second-specific behaviour here
}
}
}
Мы могли бы сделать с некоторой поддержкой языка, чтобы сделать это автоматически, - и есть много вариантов, которые я здесь не показывал, например, фактически не использовал вложенный класс для всех значений или не использовал один и тот же вложенный класс для несколько значений, но давая им разные параметры конструктора. Но в принципе, тот факт, что вложенный класс может вызвать частный конструктор, дает много мощности.
Ответ 2
Framework Design Guidelines содержит лучшие правила для использования вложенных классов, которые я нашел на сегодняшний день.
Вот краткий сводный список:
-
Использовать вложенные типы, когда отношения между типом и вложенным типом являются такой семантикой доступности элементов.
-
НЕ использовать общедоступные вложенные типы как конструкцию логической группы
-
Не используйте общедоступные вложенные типы.
-
НЕ используйте вложенные типы, если тип, вероятно, будет указан за пределами содержащего типа.
-
НЕ используйте вложенные типы, если им нужно создать экземпляр клиентским кодом.
-
НЕ задайте вложенный элемент как член интерфейса.
Ответ 3
Вы должны ограничить обязанности каждого класса, чтобы каждый из них оставался простым, проверяемым и многоразовым. Частные внутренние классы идут вразрез с этим. Они вносят вклад в сложность внешнего класса, они не проверяются и не могут использоваться повторно.
Ответ 4
Для меня лично я создаю только частные внутренние классы, если мне нужно создать внутрипроцессные коллекции объекта, которые могут потребовать от них методы.
В противном случае это может вызвать путаницу для других разработчиков, работающих над проектом, чтобы действительно найти эти классы, поскольку они не очень понятны, где они находятся.