Класс, объявленный внутри другого класса в С#
Я работаю над некоторым устаревшим кодом и сталкиваюсь с тем, о чем я не уверен. У нас есть class y
, который объявлен внутри другого class x
. class y
используется только внутри class x
, но мой вопрос в том, почему бы вам не создать отдельный файл класса и не поставить там class y
вместо того, чтобы объявить его внутри class x
? Разве это не нарушает ООП или это просто вопрос стиля, поскольку он используется только внутри этого класса. Я рефакторинг некоторых из этого кода, и моя первая реакция будет заключаться в том, чтобы отделить class y
от него в собственном файле.
namespace Library
{
public class x
{
// methods, properties, local members of class x
class y
{
// methods, properties, local members of class y
}
}
}
Ответы
Ответ 1
Вы создаете внутренний класс, потому что он используется только в пределах класса x и он логически подходит для факторинга/архитектуры класса x.
Класс y также может быть посвящен в детали реализации класса x, которые не должны быть известны общественности.
Ответ 2
Это имеет последствия для разрешений. Верхний уровень "класс y" будет "внутренним", однако здесь "y" является закрытым для "x". Этот подход полезен для деталей реализации (например, строк кеша и т.д.). Аналогично, y
имеет доступ ко всем частным состояниям x
.
Есть также последствия с дженериками; x<T>.y
является общим "из T", унаследованным от внешнего класса. Вы можете увидеть это здесь, где Bar
имеет полное использование T
- и обратите внимание, что любые статические поля Bar
ограничены в пределах T
.
class Foo<T> {
void Test(T value) {
Bar bar = new Bar();
bar.Value = value;
}
class Bar {
public T Value { get; set; }
}
}
Часто люди неправильно думают, что им нужно определить Bar
как Bar<T>
- это теперь (эффективно) вдвойне общее - т.е. Foo<TOld, T>
- где TOld
является (теперь недоступным) T
из Foo<T>
, Так что не делай этого! Или, если вы хотите, чтобы он был дважды родовым, выберите разные имена. К счастью, компилятор предупреждает вас об этом...
Ответ 3
Этот код отлично подходит по той причине, что вы указали - "класс y используется только внутри класса x". Это вложенные типы, и одно из рекомендаций по их использованию состоит в том, что вложенные типы должны быть тесно связаны с их типом объявления и не должны использоваться в качестве тип общего назначения. Таким образом, вложенный класс неприемлем для других классов, но все же позволяет вам следовать объектно-ориентированным принципам.
Ответ 4
Я думаю, что это нормально, если только содержащийся класс используется только как утилита. Я использую эту конструкцию, например, для определения сложных типов возвращаемых данных для частных методов.
Ответ 5
Я просто просмотрел код, который я обновляю (и я изначально написал), и удалил все вложенные классы. К сожалению, я изначально использовал вложенный класс вне класса, в котором он был определен. Перемещение вложенных классов внесло для меня огромное значение, потому что изначально у меня был плохой дизайн.
Если Y используется только в X и никогда не будет использоваться вне X, я бы сказал, держите его там
Ответ 6
Позвольте мне привести пример использования вложенных классов, которые могут уточнить, когда такая архитектура подходит. Недавно мне нужно было создать таблицу HTML, вытащив выбранные столбцы из таблицы данных и "развернув" их, чтобы строки стали столбцами и наоборот. В моем случае были две основные операции: поворот данных и создание некоторого довольно сложного вывода (я не просто показывал данные: каждая строка столбца/таблица данных подчинялась операциям для извлечения заголовка, генерированию тегов изображений, настройке ссылок, и т.д., поэтому использование SQL Pivot было не совсем правильным).
После первоначальной попытки создать один класс для выполнения всего этого, я понял, что большая часть данных/методов попала в три разных раздела: обработка заголовков, обработка строк и поворот. Таким образом, я решил, что лучшим подходом будет инкапсуляция логики для "заголовка" и "строки" в отдельные вложенные классы. Это позволило мне отделить данные, хранящиеся в каждой строке, и запрограммировать операции поворота очень чисто (вызов отдельного объекта строки для каждого столбца в вашей таблице данных). По завершении операций поворота я сгенерировал вывод, вызвав объект заголовка, а затем каждый объект строки, в свою очередь, чтобы генерировать свой вывод обратно в основной класс.
Отдельные классы не были подходящими, потому что A) вложенным классам нужны некоторые данные из мастер-класса, а B) обработка была очень конкретной и не была полезной в других местах. Просто программирование одного большого класса было просто беспорядочным из-за путаницы вокруг таких терминов, как "столбец" и "строка", которые отличались в зависимости от того, говорили ли вы о данных или выходе HTML. Кроме того, это была необычная работа в том, что я создавал HTML в своем бизнес-классе, поэтому я хотел разделить чистую бизнес-логику с поколением пользовательского интерфейса. В конце концов, вложенные классы обеспечивали идеальный баланс, а затем инкапсуляцию и обмен данными.
Ответ 7
Вы все равно можете реорганизовать свой класс y в другой файл, но используйте класс parial. Преимущество этого заключается в том, что у вас все еще есть один класс для каждого файла и у него нет проблем с рефакторингом перемещения объявления за пределами класса x.
например. у вас может быть файл кода: x.y.cs, который будет выглядеть примерно как
partial class X
{
class Y
{
//implementation goes here
}
}