Почему мне когда-либо понадобится использовать вложенные классы С#

Я пытаюсь понять о вложенных классах в С#. Я понимаю, что вложенный класс - это класс, который определен в другом классе, но я не понимаю, почему мне это нужно.

Ответ 1

Образец, который мне особенно нравится, заключается в объединении вложенных классов с шаблоном factory:

public abstract class BankAccount
{
  private BankAccount() {} // prevent third-party subclassing.
  private sealed class SavingsAccount : BankAccount { ... }
  private sealed class ChequingAccount : BankAccount { ... }
  public static BankAccount MakeSavingAccount() { ... }
  public static BankAccount MakeChequingAccount() { ... }
}

Вложенные классы как это, я делаю невозможным для третьих сторон создавать свои собственные подклассы. Я полностью контролирую весь код, который работает в любом объекте bankaccount. И все мои подклассы могут совместно использовать детали реализации через базовый класс.

Ответ 2

Цель обычно заключается в том, чтобы ограничить область вложенного класса. Вложенные классы по сравнению с обычными классами имеют дополнительную возможность модификатора private (а также protected, конечно).

В принципе, если вам нужно всего лишь использовать этот класс из класса "родительский" (с точки зрения объема), то обычно его следует определять как вложенный класс. Если этот класс, возможно, потребуется использовать без сборки/библиотеки, обычно пользователю удобнее определять его как отдельный (родной) класс, независимо от того, существует ли какая-либо концептуальная связь между этими двумя классами. Хотя технически возможно создать класс public, вложенный в родительский класс public, это, на мой взгляд, редко подходит для реализации.

Ответ 3

Вложенный класс может иметь модификаторы доступа private, protected и protected internal вместе с public и internal.

Например, вы реализуете метод GetEnumerator(), который возвращает объект IEnumerator<T>. Потребители не будут заботиться о фактическом типе объекта. Все, что они знают об этом, это то, что он реализует этот интерфейс. Класс, который вы хотите вернуть, не имеет прямого использования. Вы можете объявить этот класс как private вложенный класс и вернуть его экземпляр (на самом деле, как компилятор С# реализует итераторы):

class MyUselessList : IEnumerable<int> {
    // ...
    private List<int> internalList;
    private class UselessListEnumerator : IEnumerator<int> {
        private MyUselessList obj;
        public UselessListEnumerator(MyUselessList o) {
           obj = o;
        }
        private int currentIndex = -1;
        public int Current {
           get { return obj.internalList[currentIndex]; }
        }
        public bool MoveNext() { 
           return ++currentIndex < obj.internalList.Count;
        }
    }
    public IEnumerator<int> GetEnumerator() {
        return new UselessListEnumerator(this);
    }
}

Ответ 4

я не понимаю, почему мне это нужно делать

Думаю, вам никогда не понадобится . Для такого вложенного класса...

class A
{
  //B is used to help implement A
  class B
  {
    ...etc...
  }
  ...etc...
}

... вы всегда можете переместить внутренний/вложенный класс в глобальную область, например...

class A
{
  ...etc...
}

//B is used to help implement A
class B
{
  ...etc...
}

Однако, когда B используется только для реализации A, тогда создание B внутреннего/вложенного класса имеет два преимущества:

  • Он не загрязняет глобальную область (например, клиентский код, который может видеть, что A не знает, что класс B даже существует)
  • Методы B неявно имеют доступ к закрытым членам A; тогда как если B не были вложены внутри A, B не сможет получить доступ к членам A, если эти члены не являются внутренними или публичными; но затем заставить этих членов внутренне или публично подвергать их другим классам (не только B); поэтому вместо этого сохраните эти методы A private и позвольте B получить доступ к ним, объявив B как вложенный класс. Если вы знаете С++, это похоже на то, что в С# все вложенные классы автоматически являются "друзьями" класса, в котором они содержатся (и что объявление класса вложенным является единственным способом объявить дружбу на С#, поскольку С# не имеет ключевого слова friend).

Когда я говорю, что B может получить доступ к закрытым членам A, что при условии, что B имеет ссылку на A; который он часто делает, поскольку вложенные классы часто объявляются как это...

class A
{
  //used to help implement A
  class B
  {
    A m_a;
    internal B(A a) { m_a = a; }
    ...methods of B can access private members of the m_a instance...
  }
  ...etc...
}

... и построен по методу A, используя такой код...

//create an instance of B, whose implementation can access members of self
B b = new B(this);

Вы можете увидеть пример ответа Мехридада.

Ответ 5

Существует также хорошее использование публичных вложенных элементов...

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

В этом примере FirstNameComparer имеет доступ к частному элементу _firstName, который не будет, если класс является отдельным классом...

public class Person
{
    private string _firstName;
    private string _lastName;
    private DateTime _birthday;

    //...

    public class FirstNameComparer : IComparer<Person>
    {
        public int Compare(Person x, Person y)
        {
            return x._firstName.CompareTo(y._lastName);
        }
    }

}

Ответ 6

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

В качестве примера - до добавления выхода к С# одним из способов реализации перечислений было включение реализации перечислителя в качестве частного класса в коллекции. Это обеспечит легкий доступ к членам коллекции, но внешнему миру не потребуется/видеть детали того, как это реализовано.

Ответ 7

Вложенные классы очень полезны для реализации внутренних деталей, которые не должны отображаться. Если вы используете Reflector для проверки таких классов, как Dictionary < Tkey, TValue > или Hashtable вы найдете несколько примеров.

Ответ 8

Может быть, это хороший пример того, когда использовать вложенные классы?

// ORIGINAL
class ImageCacheSettings { }
class ImageCacheEntry { }
class ImageCache
{
    ImageCacheSettings mSettings;
    List<ImageCacheEntry> mEntries;
}

и

// REFACTORED
class ImageCache
{
    Settings mSettings;
    List<Entry> mEntries;

    class Settings {}
    class Entry {}
}

PS: Я не принимал во внимание, какие модификаторы доступа должны применяться (частные, защищенные, общедоступные, внутренние)