Что такое причины, почему можно было бы хотеть использовать вложенные классы? [дубликат]

25
задан 3 revs 23 May 2017 в 12:34
поделиться

14 ответов

Вы ответили на свой вопрос.Используйте вложенные классы, когда вам нужен вспомогательный класс, который не имеет смысла вне класса; особенно, когда вложенный класс может использовать частные детали реализации внешнего класса.

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

Я постоянно делаю вложенные классы, потому что мне часто нужно инкапсулировать функциональность в помощнике, который не имеет смысла вне класса и может использовать частные детали реализации внешнего класса. Например, я пишу компиляторы. Недавно я написал класс SemanticAnalyzer, который выполняет семантический анализ деревьев синтаксического анализа. Один из его вложенных классов - LocalScopeBuilder. При каких обстоятельствах мне потребуется создать локальную область видимости, если я не анализирую семантику дерева синтаксического анализа? Никогда. Этот класс полностью является деталью реализации семантического анализатора. Я планирую добавить больше вложенных классов с такими именами, как NullableArithmeticAnalyzer и OverloadResolutionAnalyzer, которые также бесполезны вне класса, но я хочу инкапсулировать правила языка в этих конкретных классах.

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

Паттерн, который я использую довольно часто, состоит в том, чтобы иметь частные вложенные классы, расширяющие их внешний класс:

abstract public class BankAccount
{
    private BankAccount() { }
    // Now no one else can extend BankAccount because a derived class
    // must be able to call a constructor, but all the constructors are
    // private!
    private sealed class ChequingAccount : BankAccount { ... }
    public static BankAccount MakeChequingAccount() { return new ChequingAccount(); }
    private sealed class SavingsAccount : BankAccount { ... }

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

30
ответ дан 28 November 2019 в 17:57
поделиться

Возвращение интерфейса вызывающей стороне, реализацию которой вы хотите скрыть.

public class Outer
{
    private class Inner : IEnumerable<Foo>
    {
        /* Presumably this class contains some functionality which Outer needs
         * to access, but which shouldn't be visible to callers 
         */
    }

    public IEnumerable<Foo> GetFoos()
    {
        return new Inner();
    }
}
11
ответ дан 28 November 2019 в 17:57
поделиться

Private вспомогательные классы - хороший пример.

Например, объекты состояния для фоновых потоков. Нет веских причин раскрывать эти типы. Определение их как частных вложенных типов кажется довольно простым способом справиться с ситуацией.

7
ответ дан 28 November 2019 в 17:57
поделиться

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

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

Что касается одного класса на файл, вы можете создавать частичные классы с помощью ключевого слова partial, что я обычно и делаю.

6
ответ дан 28 November 2019 в 17:57
поделиться

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

6
ответ дан 28 November 2019 в 17:57
поделиться

"приватные вложенные классы" могут быть весьма полезны

Обратите внимание на приватные в этом предложении. Публичные вложенные типы не рекомендуются.

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

5
ответ дан 28 November 2019 в 17:57
поделиться

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

public class MyCollection : IEnumerable
{
  public IEnumerator GetEnumerator()
  {
    return new MyEnumerator();
  }

  private class MyEnumerator
  {
  }
}
3
ответ дан 28 November 2019 в 17:57
поделиться

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

«Ну, если SRP - ваша цель, почему бы не разделить их на разные классы? « Вы будете делать это в 80% случаев, но как насчет ситуаций, когда создаваемые вами классы бесполезны для внешнего мира? Вам не нужны классы, которые будете использовать только вы, чтобы загромождать API вашей сборки.

«Ну, разве не для этого внутренний Конечно. Примерно в 80% таких случаев. Но как насчет внутренних классов, которые должны получать доступ или изменять состояние общедоступных классов? Например, тот класс, который был разбит на один или несколько внутренних классов, чтобы удовлетворить вашу серию SRP? Вам также нужно будет пометить все методы и свойства для использования этими внутренними классами как internal .

«Что в этом плохого?» Ничего. Примерно в 80% таких случаев. Конечно, теперь вы загромождаете внутренний интерфейс ваших классов методами / свойствами, которые используются только для тех классов, которые вы создали ранее. И теперь вам нужно беспокоиться о том, что другие люди в вашей команде, пишущие внутренний код, не испортят ваше состояние, если будут использовать эти методы способами, которых вы не ожидали.

Внутренние классы могут изменять состояние любого экземпляра типа, в котором они определены. Таким образом, без добавления членов к определению вашего типа ваши внутренние классы могут работать с ними по мере необходимости. Что, примерно в 14 случаях из 100, будет вашим лучшим выбором для сохранения ваших типов чистыми, вашего кода надежным / поддерживаемым, а ваши обязанности - единственными.

2
ответ дан 28 November 2019 в 17:57
поделиться

Я нашел несколько случаев, когда они были весьма удобны:

  1. Управление сложным частным состоянием, таким как InterpolationTriangle, используемый классом Interpolator. Пользователю интерполятора не нужно знать, что он реализован с использованием триангуляции Делоне, и уж точно не нужно знать о треугольниках, поэтому структура данных является частным вложенным классом.

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

  3. Я сталкивался с несколькими случаями, когда фреймворк ожидает, что класс будет происходить от какого-то базового класса (например, DependencyObject в WPF), но вы хотите, чтобы ваш класс наследовался от другой базы. Можно взаимодействовать с фреймворком, используя частный вложенный класс, который происходит от базового класса фреймворка. Поскольку вложенный класс может получить доступ к частному состоянию (вы просто передаете ему родительский 'this' при создании), вы можете использовать это для реализации бедного множественного наследования через композицию.

5
ответ дан 28 November 2019 в 17:57
поделиться

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

Я не увидел ответа на ваш вопрос о one-class-per-file. Вы можете решить эту проблему, сделав внешний класс частичным, а определение внутреннего класса перенести в отдельный файл.

OuterClass.cs:

namespace MyNameSpace
{
    public partial class OuterClass
    {
        // main class members here
        // can use inner class
    }
}

OuterClass.Inner.cs:

namespace MyNameSpace
{
    public partial class OuterClass
    {
        private class Inner
        {
            // inner class members here
        }
    }
}

Вы даже можете использовать вложенность элементов Visual Studio, чтобы сделать OuterClass.Inner.cs "дочерним" OuterClass.cs, чтобы избежать загромождения проводника решений.

2
ответ дан 28 November 2019 в 17:57
поделиться

Они действительно хороши, например, для реализации паттерна singleton.

У меня есть пара мест, где я использую их для "добавления" ценности. У меня есть мультиселективный комбобокс, где мой внутренний класс хранит состояние флажка и элемента данных.

1
ответ дан 28 November 2019 в 17:57
поделиться

Частные анонимные вложенные классы необходимы для обработчиков событий в GUI.

Если какой-то класс не является частью API, экспортируемого другим классом, его необходимо сделать приватным. В противном случае вы раскрываете больше, чем намереваетесь. Примером тому служит "ошибка на миллион долларов". Большинство программистов слишком халатно относятся к этому.

Peter

0
ответ дан 28 November 2019 в 17:57
поделиться

Вопрос задан в теге C#, поэтому я не уверен, что это представляет интерес, но в COM вы можете использовать внутренние классы для реализации интерфейсов, когда класс C++ реализует несколько интерфейсов COM... по сути, вы используете это для композиции, а не для множественного наследования.

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

0
ответ дан 28 November 2019 в 17:57
поделиться

Если объекту необходимо вернуть некоторую абстрактную информацию о своем состоянии, может подойти частный вложенный класс. Например, если Fnord поддерживает методы «сохранить контекст» и «восстановить контекст», может быть полезно, чтобы функция «сохранить контекст» возвращала объект типа Fnord.SavedContext. Правила доступа типа не всегда самые полезные; например, кажется трудным предоставить Fnord доступ к свойствам и методам Fnord.SavedContext, не сделав такие свойства и методы видимыми для посторонних. С другой стороны, можно было бы Fnord.CreateSaveContext просто создать новый Fnord.SaveContext с Fnord в качестве параметра (поскольку Fnord.SaveContext может обращаться к внутренним компонентам Fnord), а Fnord.LoadContextFrom () может вызывать Fnord.SaveContext.RestoreContextTo ().

0
ответ дан 28 November 2019 в 17:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: