Один класс на файловое правило в .NET? [закрыто]


185

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

Другой аргумент, который я слышу все время: «Даже Microsoft не делает этого, так почему мы должны?»

Какой общий консенсус по этому поводу? Есть ли случаи, когда этого следует избегать?


1
возможно дублирование классов C # в отдельных файлах?

Ответы:


176

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


4
Больше классов в одном и том же файле увеличивает разницу между связанными классами только в одной операции.
Лука

3
Правильные средства просмотра различий позволяют вам просматривать все различия одного коммита.
Dykam

44
Я не совсем уверен, как этот конкретный ответ является ответом на вопрос (ы) оригинального сообщения. Конечно, это дает основание сохранить один класс в файле (хотя Лука и Дайкам хорошо зарекомендовали себя), но это не отражает общего консенсуса и не дает случаев, когда его следует избегать.
Роберт Дэвис

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

263

Я ненавижу, когда люди думают абсолютно и говорят, что вы никогда не должны делать то или иное с чем-то субъективным и придирчивым, как это, как если бы нам всем нужно было подчинить кому-то глупое представление о добре и зле. Итог: иметь более одного класса на файл вполне нормально, если это имеет смысл. Под смыслом я имею в виду такие вещи, как:

  1. Делает код легче переваривать и поддерживать
  2. Делает решение менее раздражающим (прокручивая бесчисленные ненужные файлы) и менее медленным
  3. Команда разработчиков в порядке с местной практикой кодирования

Действительно хороший пример того, почему мне может понадобиться несколько классов на файл:

Скажем, у меня есть несколько десятков пользовательских классов исключений, каждый из которых имеет 4 строки, у меня может быть отдельный файл для каждого или я могу сгруппировать исключения и иметь файл для каждой группы. Для меня то, что кажется наиболее рациональным / прагматичным подходом, - это сгруппировать их и просто иметь несколько файлов, потому что это более эффективно с точки зрения времени / кодирования (мне не нужно щелкать правой кнопкой мыши -> Добавить класс, переименовывать, 50 раз). , это сохраняет решение менее загроможденным и более эффективным.


92
+1000. Понимание обоснования лучших практик и двойное обдумывание перед тем, как их нарушать, - это здорово. Рабская приверженность - это зло.
dsimcha

19
Несколько десятков пользовательских классов исключений ? Разве это не реальный источник проблемы? Я не просто хочу быть придирчивым: я думаю, что в большинстве случаев люди хотят объединить классы в один файл, потому что они без необходимости создают слишком много типов. (Сказав это, возможно, есть фактический случай, когда несколько десятков пользовательских классов исключений имеют смысл). Многочисленные небольшие классы, которые не используются, обычно являются признаком более серьезной проблемы.
Джефф Стерн Старт

16
Я согласен с вами по большей части. Но исключения - это особый случай. Поскольку правильно спроектированная система должна иметь надлежащую обработку исключений, чтобы иметь дело со всеми случаями, приводящими к допустимым ошибкам, в большинстве прочитанных мной лучших статей подчеркивается необходимость создания конкретных исключений (а иногда вам нужно их много для охватите все бизнес-правила для большого проекта), чтобы вы в конечном итоге не перехватили system.exception, которая вообще не является надлежащей обработкой исключений.
Джеймс

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

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

75
static bool GeneralRuleShouldBeFollowed(IGeneralRule rule, IUseCase useCase)
{
    return (rule.Overhead(useCase) 
            < My.PersonalThresholds.ConformismVsPracticality);
}

4
Есть ли такое правило в .NET? Я бы тоже только что вернул результат заявления. : O
Джоан Венге

50

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

Общая «лучшая практика» - иметь один файл на класс.


7
Если вы признаете, что они связаны, почему вы не разъединяете их?
Мэтт

39
@ Мэтт: это называется избегать переподготовки. Если у вас есть несколько тесно связанных классов и их разделение добавит много сложности по сравнению с практической гибкостью, которую это обеспечивает, то какой в ​​этом смысл?
dsimcha

9
Иногда мне нравятся классы «данных», которые используются только этим одним классом, поэтому я обычно помещаю класс данных в тот же файл, что и пользователь, потому что datac-lass обычно практически не имеет никакой логики и выглядит как тратить на создание нового файла для него.
Earlz

3
@ Мэтт: Иногда есть вспомогательные классы, которые я бы назвал приемлемыми для связи. Они уменьшают сложность для части другого класса, но все же облегчают очень специфическую цель для этого класса.
Джордан Пармер

6
@TheMatt: Отделите это: msdn.microsoft.com/en-us/library/… Связь между модулями плохая, но сотрудничество между классами внутри логической функции неизбежно.
Бен Фойгт

25

Помимо гипотетических аргументов и сосредоточения внимания на Windows .NET с Visual Studio IDE и растущих программных проектах, в этом контексте просто имеет смысл иметь один класс на файл.


В общем, для визуальной ссылки ничто не сравнится с одним классом на файл. В самом деле.

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

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

class BicycleWheel {
    class WheelSpoke {
    }
}

Иначе зачем хранить несколько классов в одном файле? Аргумент «потому что они малы» или связан друг с другом , не имеет много воды, потому что в конечном итоге ваши классы будут связаны с другими классами. В конечном счете, вы не можете легко определить внутреннюю организацию объектов на основе их использования. особенно когда программное обеспечение продолжает расти.

Кроме того, если вы используете папки для пространств имен, у вас никогда не будет конфликта имен файлов классов. Также удобно найти класс по имени файла в файловой системе, когда он не находится в среде разработки, такой как Visual Studio (например, если вы хотите быстро отредактировать класс с помощью Блокнота или чего-то быстрого / легкого ).

Так много веских причин ...


Спасибо, что вы подразумеваете под "хотя бы первыми их частями"? Вы имеете в виду, что вы также можете разделять вложенные классы?
Джоан Венге

1
Это косвенная ссылка на partialупомянутое мной ключевое слово.
Джон К

1
Для записи вложенных классов может быть partial msdn.microsoft.com/en-us/library/wa80x488(VS.80).aspx Я посмотрел это из любопытства.
Джон К

Это только показывает, что представление по умолчанию должно быть логическим (пространство имен / класс), а не физическим (файлы).
Дэвид Шмитт

@DavidSchmitt - это то, для чего предназначен вид классов в Visual Studio.
Зак

14

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


12

Я тоже считаю, что в один файл должен быть включен один тип.

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

RelayCommand     

и

RelayCommand<T>

Знаете ли вы, как порадовать StyleCop в этом случае?
Георгий Полевой

Не согласен, существует другое соглашение для универсальных классов, Foo 1.cs for Foo <T> `и Foo 2.cs for Foo <T, T1>`.
Ойбек

@Oybek: Что если у меня есть Foo <T>, Foo <U> и Foo <U, T>? Что теперь?
Андрей Ринея

@AndreiRinea Невозможно иметь Foo<T>иFoo<U> в одном том же пространстве имен. Но если пространства имен различаются, они обычно находятся в разных папках. Таким образом, для Foo<T>и Foo<U>должно быть Foo 1.cs and for Foo <U, T> `Foo`2.cs. Но по-прежнему один класс на файл.
Ойбек

Благодарю вас за информацию! :)
Андрей Ринея

8

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

Также для людей, которые не могут найти определенные классы, если они не все находятся в файлах, названных так, как они есть, есть ли причина, по которой вы не используете Поиск, просматривая все решение? Использование Find экономит мое драгоценное время при просмотре Solution Explorer.


Re: найти - когда я ищу определение типа, я не хочу видеть, где он используется. Кроме того, поиск занимает значительно больше времени, чем прокрутка отсортированного по алфавиту списка файлов, которые я, вероятно, уже разделил (по пространству имен).
Джефф Стернал

1
Я заметил, что вы указали, что работаете с тем, что вы разделили по пространству имен. К сожалению, нам не все везет, чтобы всегда работать с проектами, которыми мы смогли управлять.
Джейсон М

6

Независимо от того, насколько легкий контент, я думаю, один класс / интерфейс / и т. Д. На файл имеет важное значение.

Если я работаю над большим решением в Visual Studio, я хочу иметь возможность видеть файлы, и мне не нужно углубляться в них, чтобы увидеть. Даже с такими инструментами навигации, как ReSharper, мне нужно отображение 1: 1.

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


5

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


5

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

Итак, дело в том, что следует использовать усмотрение !!!


1
Истинная мудрость, никакое правило в программировании не сложно и быстро.
ChaosPandion

4

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


Я считаю этот аргумент менее ценным с такими инструментами, как ReSharper. Я делаю CTRL + T и начинаю набирать имя типа, который я ищу.
Марк

3
Да, но хотите ли вы, чтобы структура вашего приложения зависела от стороннего инструмента?
Магнус

1
@ Магнус Я не говорил, что это не причина не делать этого, а менее веский аргумент для того, чтобы кто-то это сделал.
Марка

4

Инструмент StyleCop для C # имеет стандартные правила, которые требуют не более одного класса верхнего уровня в одном пространстве имен (плюс любое количество интерфейсов, делегатов и перечислений в этом пространстве имен).

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


3
Когда вы говорите «не более одного класса верхнего уровня в одном пространстве имен», вы имеете в виду один namespaceблок, верно?
Даниэль Приден

1
Правила, на которые ссылаются: SA1403: документ AC # может содержать только одно пространство имен. SA1402: документ AC # может содержать только один класс на корневом уровне, если только все классы не являются частичными и относятся к одному типу.
Стив Гилхэм

4

Иногда один класс на файл, но ...

Когда несколько классов строго связаны, более одного класса в одном и том же исходном файле, ИМХО, ЛУЧШЕ, чем выделение короткого исходного файла для каждого класса. Источник более читабелен и компактен (и с помощью #region тот же источник может быть более структурированным, чем раньше).

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


Конечно, вы должны стараться избегать занятий с таким количеством строк кода. Я бы сказал, что даже 1000+ становится слишком большим. Я также понимаю, что это не всегда возможно в устаревшем коде.
Питер

Если вы попытаетесь сгенерировать исходный код (в моем случае это генерация привязки C # для OpenGL из ее спецификации, имеющей тысячи точек входа), трудно придумать небольшой класс для большого количества данных ...
Luca

3

Иногда я оставляю небольшой класс с большим классом, но только если они очень тесно связаны, как объект и его класс коллекции или фабрика.

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

то есть.

  • в понедельник я делаю изменения в своих классах х и у в файле y.css
  • во вторник я разделяю класс x на его собственный файл x.css, потому что он стал большим
  • в среду мой босс хочет посмотреть, что я изменил в классе x в понедельник, поэтому он смотрит на историю для x.css, только x.css не показывает историю до изменений во вторник.

1
Что если «маленький класс» является чем-то вроде производной EventArgs, которая предназначена для передачи информации получателям событий вашего класса? Будет ли код сделан чище или легче поддерживать, перемещая такую ​​вещь в другой файл? Можно утверждать, что такая вещь должна быть вложенным публичным классом, но они, похоже, тоже не одобряются. Что касается истории изменений, разве класс не появился бы из ниоткуда в списке изменений, и не должен ли быть комментарий в примечании к коду, откуда он взялся?
Суперкат

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

3

Это действительно проблема? :)
Действительно маленькие классы, как и перечисления, могут быть объединены с другими. Есть одно правило: соберите вместе только те классы, которые имеют что-то общее.

Как отступление - в одном из моих проектов у меня есть файл, который содержит 150 классов внутри. Файл имеет 10000 строк кода. Но это автоматически генерируется, так что это вполне приемлемо :)


1
У меня есть тот же файл с 120 классами и 750 подклассами с полмиллиона строк, он также автоматически генерируется. Проблема в том, что: если я щелкаю по нему, я должен перезагрузиться, потому что все останавливается.
Behrooz

3

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


1
Что вы подразумеваете под «образцом импортной декларации»? «Используя заявления»? Но разве классы не будут в одном и том же пространстве имен?
Джоан Венге

3
@Joan: я имею в виду необходимость импортировать 15 различных, но связанных модулей, чтобы сделать что-то действительно простое. Может быть, это не относится конкретно к C #. Я действительно не знаю C #, но на других языках это довольно раздражающая проблема.
dsimcha

Спасибо, да, именно поэтому я был смущен.
Джоан Венге

2

Я делаю это, но только тогда, когда классы связаны по типу дочерний-родитель, а дочерние классы используются ТОЛЬКО родителем.


Спасибо, но почему бы вам не разделить его на другой файл? Просто любопытно.
Джоан Венге

@henchman сказал это намного красноречивее, чем я, особенно когда дочерний объект очень маленький. Обычно это тот случай, когда дочерний класс обладает свойствами только с небольшой логикой
CResults,

2

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

  • EventArgs.cs, который содержит любые EventArgsподклассы, так как они обычно содержат всего 5-10 строк кода, но обычно они используются несколькими различными классами. В качестве альтернативы я мог бы поместить EventArgsклассы в тот же файл, что и класс, который объявляет события.
  • Delegates.cs, который содержит делегаты, которые используются в проекте, так как обычно они содержат только одну строку. Опять же, альтернатива состоит в том, чтобы поместить их в один файл с классом, который выставляет / потребляет их.
  • Enums.cs, содержащий файлы, enumиспользуемые в проекте. (Если есть, enumкоторый используется только одним классом, я обычно делаю это privateк этому классу.)

2

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


2

Я следую за этим 99% времени. Хорошо следовать стандартам, но я также верю, что гибкость имеет место. Иногда это кажется глупой тратой времени, чтобы разбить вещи на части. В те времена я перебираю себя и просто пишу свой код.


2

До сих пор ответы, похоже, вращались вокруг исключений людей из правила, поэтому вот мое: я держу вместе классы и их «меткие» классы метаданных при использовании пакета DataAnnotations в .NET3.5 SP1. В противном случае они всегда в отдельных файлах. Вы знаете, большую часть времени. За исключением случаев, когда они не.


2

Я делаю это редко. Например, если есть перечисление или структура, которые тесно связаны с классом, но слишком тривиальны для самостоятельного разделения.

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


1

One case could be:когда ваши классы совместно образуют, module / unitчто служит некоторым основным классам, как helper classes, другим мудрым нет .

взгляните на исходный код проекта ASP.NET MVC 2.0 . Строго следует этому правилу


1

Мне нравится идея создавать меньшие классы и следить за тем, чтобы класс делал только то, что должен делать. Если у вас есть несколько классов, которые вносят свой вклад в решение одной проблемы, то нет смысла объединять их в один файл.

Я не стал бы следовать практикам РС, поскольку они не ЛУЧШИЕ ПРАКТИКИ!


1

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

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

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


0

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

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

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

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

Один класс на файл - это однозначное правило, не подлежащее интерпретации.


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


-1

Назад и вперед было очень интересно и, казалось бы, неубедительно, хотя мое общее впечатление таково, что отображение 1-1 между классами и файлами является мнением большинства, хотя с некоторыми личными исключениями.

Мне любопытно, зависит ли какой-либо из ваших ответов от того, являетесь ли вы: (1) разработкой приложения для Windows Forms, веб-приложения, библиотеки или чего-либо еще; или (2) с использованием Visual Studio или нет. При использовании VS может показаться, что один класс на правило файла будет также подразумевать один класс на проект VS, поскольку в других потоках предполагается, что решения / проекты VS должны отражаться в именах и структуре каталогов / файлов. Действительно, у меня сложилось впечатление, что концепция должна иметь имя проекта = имя сборки = (вложенное) имя пространства имен, которое затем будет отражено в именах и структуре каталогов / файлов. Если это правильные руководящие принципы (или правила), то все эти, казалось бы, ортогональные организационные механизмы, тем не менее, будут синхронизированы.


-1

Да, для удобства чтения у нас должен быть один файл на класс! Просто запрыгнул в проект. Я вижу много классов в одном файле. новому парню так трудно это понять. мы не должны думать о ремонтопригодности? когда мы разрабатываем программное обеспечение? Много раз разработка будет продолжена другими разработчиками. У нас есть пространства имен, чтобы упорядочить наши вещи, нам не нужны файлы для этого!


-5

Один элемент кода на файл, да.

Все остальное - злоупотребление служебным положением - и, откровенно говоря, признак жертвы RAD.

Как только кто-то начинает правильную разработку программного обеспечения (IoC, шаблоны проектирования, DDD, TDD и т. Д.) И оставляет «Боже мой, давайте сделаем это», я понятия не имею, как, но мне платят », можно увидеть, что это правило действительно, действительно, имеет значение.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.