Каковы причины, по которым вы хотели бы использовать вложенные классы?

В qaru.site/info/74870/... комментатор отметил, что " частные вложенные классы" могут быть весьма полезными, поэтому я читал о них в статьи, такие как этот, которые, как правило, объясняют, как вложенные классы функционируют технически, но не почему вы бы использовали их.

Я полагаю, что я бы использовал частные вложенные классы для маленьких вспомогательных классов, которые относятся к более крупному классу, но часто мне нужен вспомогательный класс из другого класса, поэтому мне просто нужно было бы взять дополнительный (1) сделать вложенный класс не вложенным или (2) сделать его общедоступным, а затем получить к нему доступ с префиксом внешнего класса на нем, который, как представляется, является дополнительной работой без какого-либо добавленного значения для наличия вложенного класса в первое место. Следовательно, в целом я действительно не вижу примера использования для вложенных классов, кроме, возможно, для того, чтобы классы немного организовывались в группы, но я также не согласен с one-class- per-file, которую я получил.

Каким образом вы используете вложенные классы, чтобы сделать ваш код более управляемым, читабельным, эффективным?

Ответ 1

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

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

Я все время вставляю вложенные классы, потому что я часто занимаю необходимое место для инкапсуляции функциональности в помощнике, которая не имеет смысла вне класса и может использовать частные детали реализации внешнего класса. Например, я пишу компиляторы. Недавно я написал класс 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 { ... }

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

Ответ 2

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

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();
    }
}

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

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

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

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

Ответ 8

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

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

  private class MyEnumerator
  {
  }
}

Ответ 9

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

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

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

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

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

Ответ 10

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

Одна точка, которую я не видел, была ответом на вашу озабоченность 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, чтобы избежать загромождения вашего браузера решений.

Ответ 11

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

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

Ответ 12

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

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

Петр

Ответ 13

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

Ответ 14

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

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