C #, можно ли использовать вложенные классы для логической структуры? - PullRequest
8 голосов
/ 13 сентября 2011

У меня небольшая дискуссия об использовании вложенных классов. Ситуация такова, что имя класса имеет смысл повторяться в двух или более местах, и хотя между каждым из этих экземпляров существует умеренное сходство, они обычно различаются. Вложенные классы не часто (если вообще нужны) выходят за рамки родительского класса.

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

class A {
   class B {
}

class M {
   class B {
   }
}

class Q {
   class B {
   }
}

Очевидная проблема - не функциональность, а последовательность / повторение. Мне было интересно, сталкивались ли когда-либо другие разработчики с такими же вещами, и каковы были некоторые мнения.

Ответы [ 11 ]

11 голосов
/ 13 сентября 2011

Руководство по проектированию .net советует против него :

  • "Не используйте публичные вложенные типы в качестве конструкции логической группировки; для этого используйте пространства имен."
  • "Избегайте открытых вложенных типов. Единственное исключение из этого - случаи, когда переменные вложенного типа должны объявляться в редких сценариях, таких как создание подклассов или другие сценарии расширенной настройки."

Это также то, что делает библиотека базовых классов: в пространстве имен System.Web.UI у вас есть DataGridItem, DataListItem, ListViewItem, MenuItem, RepeaterItem и т. Д. Все это можно назвать Item и вложенные в DataGrid, DataList и т. Д. Однако это нарушит два принципа, изложенных выше.

1 голос
/ 13 сентября 2011

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

Как правило, я бы уклонялся от использования вложенных классов в первоначальном проекте - и дважды подумал, прежде чем рассматривать их в редизайне. При обслуживании, если у вас есть время, лучше перепроектировать весь класс и разделить их на отдельные классы в отдельных внутренних файлах.

Я думаю, что эта стратегия также лучше для тестируемости , чем использование вложенных классов. Из-за больших зависимостей с внешним классом и другими классами в приложении мои рефакторированные вложенные классы были не намного проще для модульного тестирования, чем оригинальный большой класс, который передавал много параметров. Если вы разделите вложенные классы так, чтобы они были самостоятельными, вы можете написать более дискретные модульные тесты , которые фактически проверяют блоки , а не эффективно объединять модульные тесты для внешнего класса и внутреннего класса. Это даст вам больше уверенности в том, чтобы сказать: «Да, внутренний класс работает на уровне модульного теста» и «Да, внешний класс работает на уровне модульного теста» (который также проверяет, как он сочетается с внутренним классом, например, в вычислительных формулах).

1 голос
/ 13 сентября 2011

Когда ваши классы маленькие, все выглядит хорошо. Как только они раздуты, вы действительно начинаете думать о перемещении их в отдельные файлы.

Более того, если вы хотите использовать A.B и M.B в одном и том же коде, вы всегда должны вводить A.B и M.B, что может быть неприятно.

1 голос
/ 13 сентября 2011

Если класс B имеет какие-либо сходства между каждым экземпляром внутреннего класса, имеет ли смысл для вас абстрагировать сходство B с базовым классом, который существует вместе с A, M и Q ? (Я так думаю.) Тогда ваши внутренние классы, хотя они могут иметь одно и то же имя, будут немного чище.

С учетом сказанного, этот тип структуры можно увидеть для таких вещей, как метаданные в приложении MVC. В этом случае у вас будет что-то вроде:

[MetadataType(typeof(A.Metadata))]
class A
{
    protected class Metadata
    {
        ...
    }
}

[MetadataType(typeof(B.Metadata))]
class B
{
    protected class Metadata
    {
        ...
    }
}

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

Я думаю, что нетипично видеть это на практике иначе. Я уверен, что есть хорошие примеры, но держу пари, что есть и более плохие примеры такого типа паттернов.

0 голосов
/ 13 сентября 2011

Нет ничего плохого во вложенных классах, если вы придерживаетесь следующих правил:

  1. Никогда не является публичным или внутренним. Существуют особые случаи, например, когда вы используете вложенный класс для реализации IEnumerator. Но даже в этом случае сам класс должен оставаться закрытым, поскольку его экземпляры возвращаются как IEnumerator, и на самом деле это просто делается для того, чтобы избежать объединения пространства имен с классами, для которых не предполагается создавать экземпляры.

  2. Держите их маленькими. Закрытый вложенный класс, который на самом деле просто используется для хранения и передачи данных более организованным способом, это хорошо, и иногда может быть очень полезным инструментом. (Не совсем так, как анонимные классы полезны.) Но если вы хотите использовать их для упаковки больших кусков функциональности, это становится запахом кода, который предполагает, что вы могли бы подумать о рефакторинге внешнего класса.

0 голосов
/ 13 сентября 2011

Если вы хотите назвать их одинаковыми, но разными типами, вы можете использовать разные пространства имен.

Namespace1.MyClass{}
Namespace2.MyClass{}

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

0 голосов
/ 13 сентября 2011

Не создавайте вложенный класс, если есть вероятность (или деловая причина), что вам придется использовать его в каком-то другом месте (вместо этого используйте пространство имен и не стесняйтесь, если вам нужно создать класс с длинным именем)).

Например, я использую вложенный класс для DTO между моим контроллером и моим представлением или в классе для представления записи в кэше.

0 голосов
/ 13 сентября 2011

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

class Text {
   enum Alignment

class UIElement {
   enum Alignment

или

class Quadtree {
   private class Node

class Octree {
   private class Node
0 голосов
/ 13 сентября 2011

Ну, вы также можете использовать пространства имен для таких вещей (просто создайте новую папку в VS). Что лучше для организации и в значительной степени даст тот же результат.

Но если подкласс имеет отношение только к родительскому классу, то я не вижу в этом вреда.

Опять же, если вы называете их одним и тем же, я думаю, что они делают похожее отбрасывание, и вы можете захотеть взглянуть на абстракцию, возможно, ваши родительские классы могли бы быть выполнены по-другому. Действительно зависит от того, что вам нужно сделать, хотя

0 голосов
/ 13 сентября 2011

Это действительно зависит от функциональности вложенного класса.Это очень похоже на то, как C ++ STL по-разному определяет итератор в каждом классе.В практике нет ничего плохого, если концепция каждого из них действительно различна в зависимости от класса.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...