1

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

Nowhere Man
  • 15,995
  • 33
  • 19
  • 29
evan
  • 81

2 Answers2

2

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

Один из примеров применения, это создание приватных наследников "внешнего" класса:
public class Outer
{
    public static Outer CreateSomething() => return new Inner();
private class Inner : Outer { ... }

}

В данном случае Inner не будет виден за пределами Outer класса, позволяя скрывать внутреннюю реализвацию. Из практики было такое, например, у меня есть кастомная коллекция, которая реализует ICollection<T> назовем её MyCollection и у меня появилась необходимость расширить мою реализацию и добавить возможность сделать одну коллекцию родетелем другой, т.е. при вызове myCollection[key], я сначала проверяю дочернюю коллекцию и созвращаю елемент, если его нет, тогда проверяю родительскую.

И есть несколько способов это реализовать:

  • все запихнуть в один класс - плохо, получается каша и далеко не всегда эта фича с родитель-потомок отношением нужна
  • создать нового публичного наследника MyScopedCollection, таким образом у меня появляется два класс, один в себе содержир реализацию коллекции, второй добовляет возможность добавлять её как родитель
  • создать нового приватного наследника MyScopedCollection, аналогично предидущему пунтку, но дополнительно скрываем реализацию.
public class MyColletion : ICollection<...>
{
    // реализация коллекции
public static MyColletion CreateChild(MyColletion parent)
    =&gt; new MyScopedCollection(parent);

private class MyScopedCollection : MyCollection
{
    public MyScopedCollection(MyColletion parent) { ... }
}

}

// ...

var parent = new MyCollection { ... }; var child = MyColletion.CreateChild(parent);

Реализация паттерна Memento

Этот паттерн позмоляет сохранить внутренне состояние объекта не раскрывая его, а потом при необходимости восстановить обратно. Можно использовать немного измененный пример из википерии:

class Memento
{
    private readonly string savedState;
private Memento(string stateToSave) 
    =&gt; savedState = stateToSave;

public class Originator
{
    private string state;

    public void Set(string state)
        =&gt; this.state = state;

    public Memento SaveToMemento()
        =&gt; new Memento(state);

    public void RestoreFromMemento(Memento memento)
        =&gt; state = memento.savedState;
}

}

// ...

var originator = new Memento.Originator(); originator.Set("State1");

var oldState = originator.SaveToMemento();

originator.Set("State2"); originator.RestoreFromMemento(oldState);

// originator.state == "State1"

Originator содержит некоторое состояние, а Memento позволяет сохранить его. Т.е. у Memento приватный конструктор, то создать "потдельное" состояние, а потом вызвать RestoreFromMemento не получится. Но в тоже время вложенные класс Originator имеет доступ к приватным членам внешнего класса. Поэтому мы может его созлать в SaveToMemento методе.

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

1

В C# есть вариант реализации шаблона проектирования Singleton через вложенный класс, что позволяет сделать "ленивую" потокобезопасную инициализацию instance:

public class Singleton
{
    public string Date { get; private set; }
    public static string text = "hello";
    private Singleton()
    {
        Console.WriteLine($"Singleton ctor {DateTime.Now.TimeOfDay}");
        Date = DateTime.Now.TimeOfDay.ToString();
    }
public static Singleton GetInstance()
{
    Console.WriteLine($&quot;GetInstance {DateTime.Now.TimeOfDay}&quot;);
    return Nested.instance;
}

private class Nested
{
    internal static readonly Singleton instance = new Singleton();
}

} class Program { static void Main(string[] args) { Console.WriteLine($"Main {DateTime.Now.TimeOfDay}"); Console.WriteLine(Singleton.text); } }

Конструктор Singleton пока не вызвался, он вызовется при первом обращении к Singleton.GetInstance().

CrazyElf
  • 71,194