Метод можно сделать статичным, но так ли это?

366

Решарпер любит указывать на каждую страницу asp.net несколько функций, которые можно сделать статическими. Поможет ли мне, если я сделаю их статичными? Должен ли я сделать их статическими и переместить их в служебный класс?

dlamblin
источник
20
Разве Решарпер на самом деле не кричит: «Низкая когезия, низкая когезия»? пришло время посмотреть, действительно ли метод принадлежит этому классу.
ПК

Ответы:

245

Статические методы и методы экземпляра
10.2.5 Статические и экземпляры-члены Спецификации языка C # объясняют разницу. Как правило, статические методы могут обеспечить очень небольшое повышение производительности по сравнению с методами экземпляра, но только в несколько экстремальных ситуациях (подробнее об этом см. В этом ответе ).

Правило CA1822 в FxCop или Code Analysis гласит:

«После [пометки элементов как статических] компилятор будет выдавать не виртуальные сайты вызовов этим участникам, что предотвратит проверку во время выполнения для каждого вызова, которая гарантирует, что текущий указатель объекта не равен нулю. Это может привести к ощутимому увеличению производительности для чувствительного к производительности кода. В некоторых случаях невозможность доступа к текущему экземпляру объекта представляет проблему правильности. "

Класс
утилит Вы не должны перемещать их в класс утилит, если это не имеет смысла в вашем дизайне. Если статический метод относится к определенному типу, как ToRadians(double degrees)метод относится к классу, представляющему углы, то имеет смысл, чтобы этот метод существовал как статический член этого типа (обратите внимание, это сложный пример для демонстрации).

Джефф Йейтс
источник
2
> Компилятор будет выдавать не виртуальные сайты вызовов этим участникам. На самом деле это "компилятор может испускать ...". Я помню кое-что о компиляторе C #, использующем callvirt вместо call для обхода некоторой потенциальной ошибки.
Джонатан Аллен
24
Я вырезал и вставил его прямо из FxCop 1.36. Если FxCop не прав, достаточно справедливо.
Джефф Йейтс
5
@Maxim Не уверен, что ценю утверждение "фигня"; довольно грубый способ приблизиться к незнакомцам. Тем не менее, базовый пункт является действительным; Я немного обновил вещи (это было 9 лет назад, поэтому я не помню основание моей первоначальной заявки).
Джефф Йейтс
10
@ Максим Ваша точка зрения недействительна. Уверяю вас, что у меня не было хорошего рейтинга 9 лет назад. Я ценю комментарии, которые указывают на ошибки (или исправления, которые исправляют их), но не будьте грубыми и не ставьте необоснованные ожидания другим. Не называйте что-нибудь "фигня"; это подразумевает намерение обмануть, а не честно допустить ошибку или невежество. Это грубо. Я добровольно помогаю здесь, чтобы помочь, и это действительно кажется бессмысленным, когда к нему относятся с неуважением Не говорите мне, на что обижаться - это мой выбор, а не ваш. Узнайте, как сделать свою точку зрения с уважением и честностью. Спасибо.
Джефф Йейтс
2
@Maxim Хотя делегаты не хранятся рядом с каждым экземпляром, каждый экземпляр класса без состояния действительно занимает некоторую память в куче, что бесполезно. Обычно создание экземпляров сервисов не является горячим путем в приложении, но если ваше приложение завершает создание большого количества этих объектов, оно создает давление GC, которого можно избежать, просто используя статические методы. Первоначальное утверждение OP о том, что в экстремальных ситуациях статические методы обеспечивают выигрыш в производительности по сравнению с экземплярами без сохранения состояния, было соответствующим образом продумано и обосновано.
Асад Саидуддин
259

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

Иногда есть методы , которые логически действуют на экземпляре , но не бывают , чтобы использовать любого из состояния экземпляра еще . Например, если вы строите файловую систему и у вас есть понятие каталога, но вы еще не реализовали его, вы можете написать свойство, возвращающее тип объекта файловой системы, и оно всегда будет просто «file» - но он логически связан с экземпляром, и поэтому должен быть методом экземпляра. Это также важно, если вы хотите сделать метод виртуальным - вашей конкретной реализации может не потребоваться состояние, но могут производные классы. (Например, спрашивая коллекцию, доступна ли она только для чтения - возможно, вы еще не внедрили форму этой коллекции только для чтения, но это явно свойство самой коллекции, а не ее типа.)

Джон Скит
источник
1
Я думаю, что хороший линтер должен иметь возможность ограничить сообщение не виртуальными методами, поскольку для метода базового класса было бы очень распространенным делом делать практически ничего. Методы переопределения обычно делают что-то, но не всегда. Иногда полезно иметь класс для чего-то вроде пустого iEnumerable, чьи методы по существу игнорируют экземпляр, но где экземпляр необходим для выбора правильного метода для использования.
суперкат
2
«Иногда существуют методы, которые логически действуют на экземпляр, но еще не используют ни одно из состояний экземпляра. Например,« Мне понравилось, что вы использовали «например» в этом случае.
PaulBinder
56

Маркировка метода как staticвнутри класса делает очевидным, что он не использует никаких элементов экземпляра, что может быть полезно знать при просмотре кода.

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

Марк Сидаде
источник
22

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

К сожалению, это были статические методы, которые принимали определенное состояние приложения. (почему, конечно, у нас будет только один пользователь на приложение! Почему бы классу User не отслеживать это в статических переменных?) Они были прославленными способами доступа к глобальным переменным. У них также были статические конструкторы (!), Что почти всегда было плохой идеей. (Я знаю, что есть несколько разумных исключений).

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

Просто убедитесь, что вы помещаете их в нужное место. Являются ли статические методы навязчиво манипулирующими внутренним состоянием других объектов? Можно ли доказать, что их поведение относится к одному из этих классов? Если вы не разделяете проблемы должным образом, у вас могут возникнуть головные боли позже.

JasonTrue
источник
4
Ваша проблема со статическими полями / свойствами, а не со статическими методами.
Асад Саидуддин
10

Это интересно читать:

http://thecuttingledge.com/?p=57

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

но вот что говорит резче документация: http://confluence.jetbrains.net/display/ReSharper/Member+can+be+made+static

pajics
источник
2
Я думаю, что этот момент недооценен. Инструмент действительно говорит вам о том, что метод работает только с некоторыми другими членами класса. Если это какой-то командный (или «прецедент», или «интерактор») объект, то ответственность за то, чтобы манипулировать другими объектами, это нормально. Однако, если он манипулирует только одним другим классом, который звучит во многом как Feature Envy .
Грег
9

Чтобы добавить ответ @Jason True , важно понимать, что просто добавление метода «static» не гарантирует, что метод будет «чистым». Он не будет иметь состояния в отношении класса, в котором он объявлен, но он вполне может обращаться к другим «статическим» объектам, которые имеют состояние (конфигурация приложения и т. Д.), Это не всегда плохо, но одна из причин того, что Лично я склонен отдавать предпочтение статическим методам, когда могу, если они чистые, вы можете проверять и рассуждать о них изолированно, не беспокоясь об окружающем состоянии.

Benjol
источник
6

Вы должны делать то, что наиболее читабельно и интуитивно понятно в данном сценарии.

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

Эрик Шуновер
источник
6

Для сложной логики в классе я обнаружил, что частные статические методы полезны при создании изолированной логики, в которой входные данные экземпляров четко определены в сигнатуре метода, и побочные эффекты экземпляров не могут возникнуть. Все выходы должны быть через возвращаемое значение или параметры out / ref. Разбиение сложной логики на блоки кода без побочных эффектов может улучшить читабельность кода и доверие команды разработчиков к нему.

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

G-Wiz
источник
5

ReSharper не проверяет логику. Он только проверяет, использует ли метод элементы экземпляра. Если метод является закрытым и вызывается только (возможно, одним) методами экземпляра, это признак того, что он может быть методом экземпляра.

brgerner
источник
3

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

Mun
источник
3

Создание статического метода означает, что вы можете вызывать метод извне класса без предварительного создания экземпляра этого класса. Это полезно при работе с объектами сторонних поставщиков или надстройками. Представьте себе, если вам нужно было сначала создать консольный объект «con» перед вызовом con.Writeline ();

Остин
источник
Java заставит вас создать экземпляр фабрики для создания объекта Console перед вызовом con.Writeline ().
Скотт Мишо
2

Это помогает контролировать загрязнение пространства имен.

мистифицировать
источник
8
Как создание статического метода помогает избежать загрязнения пространства имен?
lockstock
1
Исходя из опыта, группируя методы в классы со статическими методами, вы избегаете опыта использования префикса всех «мешков» свободных функций, которые могут конфликтовать с другими библиотеками или встроенными функциями. С помощью статических методов они эффективно располагаются в пространстве имен под именем класса, напр. Class.a_core_function( .. )vsa_core_function( .. )
lintuxvi
0

Просто мое желание: добавление всех общих статических методов в служебный класс позволяет вам добавить

using static className; 

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

Я понятия не имею, если это хорошая практика или нет. Мне нужно так много узнать о C # 4/5 и так много унаследованного кода для рефакторинга, что я просто пытаюсь руководствоваться советами Roselyn.

детеныш

Джозеф морган
источник
0

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

Мой короткий ответ: да, вы можете преобразовать их в статические методы, если предложит Решарпер. В этом нет никакого вреда. Скорее, делая метод статичным, вы на самом деле защищаете метод, чтобы излишне не добавлять элементы экземпляра к этому методу. Таким образом, вы можете достичь принципа ООП « Минимизировать доступность классов и членов ».

Когда ReSharper предполагает, что метод экземпляра может быть преобразован в статический метод, он фактически говорит вам: «Почему .. этот метод находится в этом классе, поскольку он фактически не использует ни одно из его состояний?» Так что это дает пищу для размышлений. Тогда именно вы можете осознать необходимость перемещения этого метода в статический служебный класс или нет. Согласно принципам SOLID, класс должен иметь только одну основную ответственность. Таким образом, вы можете лучше очистить свои классы таким образом. Иногда вам нужны некоторые вспомогательные методы даже в вашем классе экземпляра. Если это так, вы можете оставить их у помощника #region.

Эмран Хуссейн
источник