Рассмотрим следующий метод:
public List<Guid> ReturnEmployeeIds(bool includeManagement = false)
{
}
И следующий звонок:
var ids = ReturnEmployeeIds(true);
Для разработчика, плохо знакомого с системой, было бы довольно сложно догадаться, что именно true
. Первое, что вы должны сделать - навести курсор на имя метода или перейти к определению (ни одна из которых не является большой задачей). Но для удобства чтения имеет смысл написать:
var ids = ReturnEmployeeIds(includeManagement: true);
Есть ли где-нибудь, где формально обсуждается, стоит ли явно указывать необязательные параметры, когда вам не нужен компилятор?
В следующей статье обсуждаются некоторые соглашения о кодировании: https://msdn.microsoft.com/en-gb/library/ff926074.aspx
Нечто похожее на статью выше было бы здорово.
c#
coding-style
readability
parameters
JᴀʏMᴇᴇ
источник
источник
boolean
аргументы метода и как?Ответы:
Я бы сказал, что в мире C # перечисление будет одним из лучших вариантов здесь.
При этом вы будете вынуждены объяснить, что вы делаете, и любой пользователь увидит, что происходит. Это также безопасно для будущих расширений;
Итак, на мой взгляд здесь:
enum> необязательный enum> необязательный bool
Редактировать: В связи с обсуждением с LeopardSkinPillBoxHat ниже относительно
[Flags]
перечисления, которое в данном конкретном случае может быть подходящим (поскольку мы специально говорим о включении / исключении вещей), я вместо этого предлагаю использоватьISet<WhatToInclude>
в качестве параметра.Это более «современная» концепция с несколькими преимуществами, в основном из-за того, что она вписывается в семейство коллекций LINQ, но также
[Flags]
имеет максимум 32 группы. Основным недостатком использованияISet<WhatToInclude>
является то, как плохо поддерживаются множества в синтаксисе C #:Некоторые из них могут быть смягчены вспомогательными функциями, как для генерации набора, так и для создания «быстрых наборов», таких как
источник
enum
подход, но я не большой поклонник микширования,Include
иExclude
в то жеenum
время мне сложнее понять, что происходит. Если вы хотите, чтобы это было действительно расширяемым, вы можете сделать это a[Flags]
enum
, сделать их всеIncludeXxx
и предоставить значение,IncludeAll
которое установлено вInt32.MaxValue
. Затем вы можете предоставить 2ReturnEmployeeIds
перегрузки - одну, которая возвращает всех сотрудников, и другую, которая принимаетWhatToInclude
параметр фильтра, который может быть комбинацией флагов enum.[Flags]
это реликвия, и ее следует использовать только из соображений наследия или производительности. Я думаю, чтоISet<WhatToInclude>
это гораздо лучшая концепция (к сожалению, в C # не хватает синтаксического сахара, чтобы его было удобно использовать).Flags
подход, потому что он позволяет вызывающей стороне передаватьWhatToInclude.Managers | WhatToInclude.Cleaners
и побитовый или точно выражает, как вызывающая сторона будет обрабатывать его (т. Е. Менеджеры или уборщики). Интуитивно понятный синтаксический сахар :-)Это спорно, если это «хороший стиль», но ИМХО это, как минимум, не плохой стиль, и полезно, пока строка кода не становится «слишком долго». Без именованных параметров это также общий стиль для введения объясняющей переменной:
Этот стиль, вероятно, более распространен среди программистов C #, чем вариант именованных параметров, поскольку именованные параметры не были частью языка до версии 4.0. Влияние на читаемость практически одинаково, просто нужна дополнительная строка кода.
Поэтому моя рекомендация: если вы думаете, что использование enum или двух функций с разными именами - это «перебор», или вы не хотите или не можете изменить сигнатуру по другой причине, продолжайте и используйте названный параметр.
источник
includeManagement
как локальныйconst
и вообще не использовать дополнительную память.Если вы столкнетесь с кодом вроде:
Вы можете в значительной степени гарантировать, что внутри
{}
будет что-то вроде:Другими словами, логическое значение используется для выбора между двумя направлениями действий: метод имеет две обязанности. Это в значительной степени гарантирует запах кода . Мы всегда должны стремиться к тому, чтобы методы делали только одно; у них только одна ответственность. Поэтому я бы сказал, что оба ваших решения неверны. Использование необязательных параметров является признаком того, что метод делает больше, чем одну вещь. Булевы параметры также являются признаком этого. Наличие обоих должно определенно установить звон будильника. Поэтому вам следует выполнить рефакторинг двух разных наборов функций в два отдельных метода. Дайте имена методов, которые проясняют, что они делают, например:
Это улучшает читаемость кода и гарантирует, что вы будете лучше следовать принципам SOLID.
РЕДАКТИРОВАТЬ Как было отмечено в комментариях, случай, когда эти два метода, вероятно, будут иметь общие функциональные возможности, и что общие функциональные возможности, вероятно, лучше всего будут обернуты в частную функцию, которая будет нуждаться в булевом параметре для управления некоторым аспектом того, что он делает ,
В этом случае, следует ли указывать имя параметра, даже если компилятору это не нужно? Я бы предположил, что нет простого ответа на этот вопрос, и это решение необходимо в каждом конкретном случае:
Является ли исходный файл и методы маленькими и понятными? Если да, то названный параметр, вероятно, составляет шум. Если нет, это может быть очень полезно. Поэтому применяйте правило в соответствии с обстоятельствами.
источник
public List<Guid> ReturnEmployeeIds(bool includeManagement) { calculateSomethingHereForBothBranches; if (includeManagement) return something else return something else }
, что разделение на два простых метода, вероятно, будет означать, что эти два метода будут нуждаться в результате первого вычисления в качестве параметра.Да, верно. Самодокументированный код - прекрасная вещь. Как указывалось в других ответах, существуют способы устранить неоднозначность вызова
ReturnEmployeeIds
, используя перечисление, имена других методов и т. Д. Однако иногда вы не можете избежать случая, но не хотите уходить и использовать они повсюду (если вам не нравится многословие Visual Basic).Например, это может помочь уточнить один вызов, но не обязательно другой.
Именованный аргумент может быть полезен здесь:
Не добавлю много дополнительной ясности (я собираюсь догадаться, что это синтаксический анализ перечисления):
Это может на самом деле уменьшить ясность (если человек не понимает, что такое емкость в списке):
Или где это не имеет значения, потому что вы используете хорошие имена переменных:
Не AFAIK. Давайте рассмотрим обе части: единственное время, когда вам нужно явно использовать именованные параметры, это потому, что компилятор требует от вас этого (обычно это устранение неоднозначности операторов). Так что кроме этого вы можете использовать их в любое время. В этой статье от MS есть несколько советов о том, когда вы можете их использовать, но это не совсем определенно https://msdn.microsoft.com/en-us/library/dd264739.aspx .
Лично я нахожу, что использую их чаще всего, когда создаю пользовательские атрибуты или заглушаю новый метод, в котором мои параметры могут изменяться или переупорядочиваться (именованные атрибуты b / c могут быть перечислены в любом порядке). Кроме того, я использую их только когда я предоставляю статическое значение inline, в противном случае, если я передаю переменную, я пытаюсь использовать хорошие имена переменных.
В конечном итоге все сводится к личным предпочтениям. Конечно, есть несколько вариантов использования, когда вам нужно использовать именованные параметры, но после этого вам нужно сделать оценочный вызов. IMO - используйте его везде, где, по вашему мнению, это поможет документировать ваш код, уменьшить двусмысленность или позволить защитить сигнатуры методов.
источник
Если параметр очевиден из (полностью определенного) имени метода и его существование естественно для того, что метод должен делать, вы не должны использовать синтаксис именованного параметра. Например, в
ReturnEmployeeName(57)
чертовски очевидно, что57
это идентификатор сотрудника, поэтому избыточно аннотировать его с помощью синтаксиса именованных параметров.С
ReturnEmployeeIds(true)
, как вы сказали, если вы не посмотрите на объявление / документацию функции, невозможно понять, что этоtrue
значит - поэтому вы должны использовать синтаксис именованных параметров.Теперь рассмотрим третий случай:
Синтаксис именованного параметра здесь не используется, но так как мы передаем переменную
ReturnEmployeeIds
, и у этой переменной есть имя, и это имя означает то же самое, что и параметр, которому мы его передаем (это часто бывает - но не всегда!) - нам не нужен синтаксис именованного параметра, чтобы понять его значение из кода.Итак, правило простое - если вы просто можете понять из вызова метода, что должен означать параметр, не используйте именованный параметр. Если вы не можете - это недостаток читабельности кода, и вы, вероятно, хотите исправить это (конечно, не любой ценой, но обычно вы хотите, чтобы код был читабельным). Исправление не обязательно должно называться параметрами - вы также можете сначала поместить значение в переменную или использовать методы, предложенные в других ответах, - если конечный результат позволяет легко понять, что делает параметр.
источник
ReturnEmployeeName(57)
сразу становится очевидным, что57
это удостоверение личности.GetEmployeeById(57)
с другой стороны ...ReturnEmployeeName
продемонстрировать случаи, когда даже без явного упоминания параметра в имени метода вполне разумно ожидать, что люди поймут, что это такое. В любом случае, примечательно, что в отличие от этогоReturnEmployeeName
, вы не можете разумно переименоватьReturnEmployeeIds
что-то, что указывает на значение параметров.Да, я думаю, что это хороший стиль.
Это имеет смысл для логических и целочисленных констант.
Если вы передаете переменную, имя переменной должно прояснить смысл. Если это не так, вы должны дать переменной лучшее имя.
Если вы передаете строку, смысл часто ясен. Например, если я вижу вызов GetFooData («Орегон»), я думаю, читатель может догадаться, что этот параметр является именем состояния.
Конечно, не все строки очевидны. Если я вижу GetFooData («M»), то, если я не знаю функцию, я не знаю, что означает «M». Если это какой-то код, такой как M = "сотрудники уровня управления", то, вероятно, у нас должно быть перечисление, а не литерал, и имя перечисления должно быть ясным.
И я полагаю, что строка может вводить в заблуждение. Может быть, «Орегон» в моем примере выше относится не к государству, а к продукту, клиенту или чему-то еще.
Но GetFooData (true) или GetFooData (42) ... это может означать что угодно. Именованные параметры - замечательный способ сделать самодокументирование. Я использовал их именно для этой цели несколько раз.
источник
Не существует каких-либо очень четких причин, почему вообще стоит использовать этот тип синтаксиса.
В общем, старайтесь не иметь логических аргументов, которые на расстоянии могут выглядеть произвольно.
includeManagement
(скорее всего) сильно повлияет на результат. Но аргумент выглядит так, как будто он имеет «небольшой вес».Обсуждение использования enum не только будет выглядеть как аргумент «имеет больший вес», но и обеспечит масштабируемость метода. Однако это может быть не лучшим решением во всех случаях, так как ваш
ReturnEmployeeIds
-метод должен масштабироваться вместе сWhatToInclude
-enum (см. Ответ NiklasJ ). Это может вызвать у вас головную боль позже.Учтите это: если вы масштабируете
WhatToInclude
-enum, но неReturnEmployeeIds
-method. Затем он может выброситьArgumentOutOfRangeException
(в лучшем случае) или вернуть что-то совершенно нежелательное (null
или пустоеList<Guid>
). Что в некоторых случаях может запутать программиста, особенно если выReturnEmployeeIds
находитесь в библиотеке классов, где исходный код недоступен.Можно предположить, что
WhatToInclude.Trainees
это сработает, если этоWhatToInclude.All
произойдет, поскольку ученики являются «подмножеством» всех.Это (конечно) зависит от того, как
ReturnEmployeeIds
это реализовано.В случаях, когда может быть передан логический аргумент, я пытаюсь вместо этого разбить его на два метода (или более, если необходимо), в вашем случае; можно извлечь
ReturnAllEmployeeIds
,ReturnManagementIds
иReturnRegularEmployeeIds
. Это охватывает все основы и абсолютно самоочевидно для тех, кто его реализует. Это также не будет иметь «масштабирующей» проблемы, упомянутой выше.Поскольку есть только два результата для чего-то, что имеет логический аргумент. Реализация двух методов требует совсем немного дополнительных усилий.
Меньше кода, редко лучше.
С учетом сказанного, в некоторых случаях явное объявление аргумента улучшает читабельность. Рассмотрим для примера.
GetLatestNews(max: 10)
,GetLatestNews(10)
это еще довольно очевидно, явная декларацияmax
поможет прояснить любую путаницу.И если вам абсолютно необходимо иметь логический аргумент, использование которого невозможно определить, просто прочитав
true
илиfalse
. Тогда я бы сказал, что:.. абсолютно лучше и более читабельно, чем:
Поскольку во втором примере значение
true
может означать абсолютно все . Но я бы постарался избежать этого аргумента.источник