«Публичные» вложенные классы или нет - PullRequest
17 голосов
/ 03 декабря 2008

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

Сравните следующие две реализации этого сценария.

Реализация 1:

class Application 
{
   Application(ApplicationSettings settings) 
   {
       //Do initialisation here
   }
}

class ApplicationSettings 
{
   //Settings related methods and properties here
}

Реализация 2:

class Application 
{
   Application(Application.Settings settings) 
   {
       //Do initialisation here
   }

   class Settings 
   {
      //Settings related methods and properties here
   }
}

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

Теперь просто представьте, что у самого класса «Настройки», в свою очередь, есть такой же «родственный» класс, и у этого класса тоже есть. Пройдите только три таких уровня, и наименование класса выходит из-под контроля в случае «не вложенного». Однако, если вы гнездитесь, вещи все равно остаются элегантными.

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

Мой вопрос: почему мы опасаемся "публичного" использования вложенных классов? Есть ли другие аргументы против такого использования?

Ответы [ 5 ]

22 голосов
/ 03 декабря 2008

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

public class Outer
{
    private Outer(Builder builder)
    {
        // Copy stuff
    }

    public class Builder
    {
        public Outer Build()
        {
            return new Outer(this);
        }
    }
}

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

Я очень часто использую шаблон в моем C # -порте протокольных буферов.

5 голосов
/ 03 декабря 2008

Вы можете использовать пространства имен для связи вещей, которые ... связаны.

Например:

namespace Diner
{
    public class Sandwich
    {
        public Sandwich(Filling filling) { }
    }

    public class Filling { }
}

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

using Diner;

...

var sandwich = new Sandwich(new Filling());

Если вы используете класс Sandwich, как если бы это было пространство имен для Filling, вы должны использовать полное имя Sandwich.Filling для ссылки на Filling.

А как ты будешь спать по ночам, зная это?

0 голосов
/ 21 декабря 2015

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

public class OrderViewModel
{
public int OrderId{ get; set; }
public IEnumerable<Product> Products{ get; set; }

public class Product {
public string ProductName{ get; set; }
public decimal ProductPrice{ get; set; }
}

}

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

0 голосов
/ 03 декабря 2008

Я в основном использую вложенные классы для тонкой настройки доступа к вложенному и / или контейнерному классу.

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

Вы также можете использовать это для управления использованием определенного класса.

Пример:

public abstract class Outer
{
  protected class Inner
  {
  }
}

Теперь в этом случае пользователь (вашего класса) может получить доступ только к классу Inner, если он реализует Outer.

0 голосов
/ 03 декабря 2008

Вы можете проверить, что Microsoft скажет по этой теме. По сути, это вопрос стиля, я бы сказал.

...