Подтвердить что ты не робот

Использование внутренних классов в С#

Каковы наилучшие методы использования и структуры внутренних классов в С#.

Например, если у меня есть очень большой базовый класс и два больших внутренних класса, я должен разделить их на отдельные (неполные классы) кодовые файлы или оставить их как один очень большой громоздкий файл кода?

Также есть ли плохая практика иметь абстрактный класс с общепринятым внутренним классом?

4b9b3361

Ответ 1

Обычно я резервирую внутренние классы для одной из двух целей:

  • Открытые классы, которые вытекают из их родительского класса, где родительский класс представляет собой абстрактную базовую реализацию с одним или несколькими абстрактными методами, и каждый подкласс представляет собой реализацию, которая выполняет конкретную реализацию. после прочтения Framework Framework и рекомендаций. Я вижу, что это отмечено как "Избегайте", однако я использую его в сценариях, похожих на перечисления - althogh, которые, вероятно, также дают плохое впечатление.

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

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

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

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

Ответ 2

У меня нет книги, но в Руководстве по дизайну Framework предлагается использовать внутренние классы public, пока клиенты не должны ссылаться на имя класса. private внутренние классы в порядке: никто не заметит их.

Плохо: ListView.ListViewItemCollection collection = new ListView.ListViewItemCollection();

Хорошо: listView.Items.Add(...);

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

Ответ 3

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

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

Ответ 4

Я думаю, что это довольно субъективно, но я, вероятно, разделил бы их в отдельных файлах кода, сделав частичный класс "хозяин".

Таким образом, вы можете получить еще больший обзор редактирование файла проекта, чтобы сделать группу файлов так же, как классы дизайнеров в Windows Формы. Я думаю, что я видел добавление Visual Studio, которое делает это автоматически для вас, но я не помню, где.

EDIT:
После некоторого поиска я обнаружил, что надстройка Visual Studio для этого называется VSCommands

Ответ 5

Относительно только того, как структурировать такого зверя...

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

// main class in file Outer.cs
namespace Demo
{
  public partial class Outer
  {
     // Outer class
  }
}

// nested class in file Outer.Nested1.cs
namespace Demo
{
  public partial class Outer
  {
    private class Nested1
    {
      // Nested1 details
    }
  }
}

Точно так же вы часто видите (явные) интерфейсы в своем собственном файле. например Outer.ISomeInterface.cs, а не по умолчанию для редактора #region.

Структура ваших проектов затем начинает выглядеть как

   /Project/Demo/ISomeInterface.cs
   /Project/Demo/Outer.cs
   /Project/Demo/Outer.Nested1.cs
   /Project/Demo/Outer.ISomeInterface.cs

Обычно, когда мы делаем это для изменения шаблона Builder.

Ответ 6

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

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

Ответ 7

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

Вы можете использовать частичные классы, чтобы переместить определение этих внутренних классов в другой файл для лучшей orgnanization. VS не автоматически группирует файлы частичного класса для вас, за исключением некоторых шаблонизированных элементов, таких как формы ASP.NET, WinForm и т.д. Вам нужно будет отредактировать файл проекта и внести в него некоторые изменения. Вы можете посмотреть на одну из существующих группировок, чтобы посмотреть, как это делается. Я считаю, что есть несколько макросов, которые позволяют группировать частичные файлы классов для вас в проводнике решений.

Ответ 8

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

Если ваши внутренние классы становятся слишком большими, я бы определенно переместил их в отдельные классы/кодовые файлы так или иначе, хотя бы для удобства обслуживания. Я должен поддерживать некоторый существующий код, где кто-то думал, что это хорошая идея использовать внутренние классы внутри внутренних классов. Это привело к тому, что внутренняя иерархия классов с глубиной 4-5 уровней. Излишне говорить, что код непроницаем и требует времени, чтобы понять, на что вы смотрите.

Ответ 9

Вот пример практического примера вложенного класса, который может дать вам представление об их использовании (добавлено несколько unit test)

namespace CoreLib.Helpers
{
    using System;
    using System.Security.Cryptography;

    public static class Rnd
    {
        private static readonly Random _random = new Random();

        public static Random Generator { get { return _random; } }

        static Rnd()
        {
        }

        public static class Crypto
        {
            private static readonly RandomNumberGenerator _highRandom = RandomNumberGenerator.Create();

            public static RandomNumberGenerator Generator { get { return _highRandom; } }

            static Crypto()
            {
            }

        }

        public static UInt32 Next(this RandomNumberGenerator value)
        {
            var bytes = new byte[4];
            value.GetBytes(bytes);

            return BitConverter.ToUInt32(bytes, 0);
        }
    }
}

[TestMethod]
public void Rnd_OnGenerator_UniqueRandomSequence()
{
    var rdn1 = Rnd.Generator;
    var rdn2 = Rnd.Generator;
    var list = new List<Int32>();
    var tasks = new Task[10];
    for (var i = 0; i < 10; i++)
    {
        tasks[i] = Task.Factory.StartNew((() =>
        {
            for (var k = 0; k < 1000; k++)
            {
                lock (list)
                {
                    list.Add(Rnd.Generator.Next(Int32.MinValue, Int32.MaxValue));
                }
            }
        }));
    }
    Task.WaitAll(tasks);
    var distinct = list.Distinct().ToList();
    Assert.AreSame(rdn1, rdn2);
    Assert.AreEqual(10000, list.Count);
    Assert.AreEqual(list.Count, distinct.Count);
}

[TestMethod]
public void Rnd_OnCryptoGenerator_UniqueRandomSequence()
{
    var rdn1 = Rnd.Crypto.Generator;
    var rdn2 = Rnd.Crypto.Generator;
    var list = new ConcurrentQueue<UInt32>();
    var tasks = new Task[10];
    for (var i = 0; i < 10; i++)
    {
        tasks[i] = Task.Factory.StartNew((() =>
        {
            for (var k = 0; k < 1000; k++)
            {
                    list.Enqueue(Rnd.Crypto.Generator.Next());
            }
        }));
    }
    Task.WaitAll(tasks);
    var distinct = list.Distinct().ToList();
    Assert.AreSame(rdn1, rdn2);
    Assert.AreEqual(10000, list.Count);
    Assert.AreEqual(list.Count, distinct.Count);
}