Почему и когда я должен сделать класс «статическим»? Каково назначение ключевого слова static на классах?

47

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

Какие преимущества я получаю от занятий static? Я имею в виду, что после объявления статического класса все равно следует объявить все члены, к которым он / она хочет иметь доступ без создания экземпляров, как статические.

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

Саид Нямати
источник
Если вы пишете в стиле Func Prog в стиле Rich Hickey, это может быть полезно.
Работа
1
На мой взгляд, статические классы - это просто костыль, с помощью которого C # пытается скрыть огромный недостаток дизайна в языке. Зачем структурировать язык вокруг этой бессмысленной идеологии «все, что должно быть в классе», только для того, чтобы затем ввести дополнительный синтаксис, чтобы вы могли обманывать и обходить это ненужное ограничение. Если бы C # только что позволил освободить функции с самого начала, не было бы необходимости в статических классах.
antred

Ответы:

32

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

Math m = new Math();

C # не должен запрещать это, но так как он не имеет смысла, он также может сказать пользователю об этом. Некоторые люди (включая меня) придерживаются философии, согласно которой языки программирования (и API-интерфейсы…) должны быть настолько ограничивающими, насколько это возможно, чтобы их было трудно использовать неправильно: единственными допустимыми операциями являются те, которые имеют смысл и (мы надеемся) правильные.

Конрад Рудольф
источник
3
Я не согласен с этой философией. Проблема в том, что мне нужно измениться. Поэтому, хотя, возможно, имело смысл ограничить его до сих пор, это ограничение препятствует нашему развитию для будущих нужд. А унаследованная библиотека, которую мы вынуждены использовать для взаимодействия со старой системой (той, которую вы написали, запечатала и проверила все классы), не предоставляет нам никакого способа расширить или изменить тот код, который вы написали. Тот факт, что у вас нет причин создавать экземпляр своего класса, не означает, что я не буду нуждаться в этом в будущем. Несмотря на то, что составление статистики для служебного класса имеет смысл, учитывайте и будущее.
SoylentGray
19
@Chad Тогда библиотека плохо спроектирована. Расширение классов, которые не были предназначены для расширения, в конечном итоге не работает, поэтому лучше всего создать класс sealedв первую очередь. Я признаю, что не все люди согласны с этим мнением, но комментарии не являются хорошим местом для обсуждения этого. Но в случае staticэтой проблемы даже не ставить себя: создание экземпляров никогда неSystem.Math будет иметь смысла.
Конрад Рудольф
1
@Konard, если вы объявляете все члены Mathкласса статическими, не объявляя Mathкласс статическими, вы все равно можете использовать его без необходимости создания экземпляров.
Саид Нимати
11
@Saeed - это правда, но вы также можете создать экземпляр класса, который не служит какой-либо цели. Что мне делать с экземпляром класса Math? Чтобы явно показать намерение класса (не нужно и не нужно создавать экземпляры), он помечается static.
Корбин март
3
@Seed Давайте перевернем это: я предлагаю вам прочитать мой ответ еще раз, поскольку вы, кажется, неправильно его поняли: ваши комментарии не связаны с моим ответом. Я никогда не говорил, что тебе нужно писать new Math().
Конрад Рудольф
24

StackOverflow имеет отличную дискуссию на эту тему . Для удобства я скопирую и вставлю его от имени его автора Марка С. Расмуссена :

Я написал свои мысли о статических классах в более ранней теме :

Я любил вспомогательные классы, заполненные статическими методами. Они сделали большую консолидацию вспомогательных методов, которые в противном случае могли бы привести к избыточности и адскому обслуживанию. Они очень просты в использовании, нет инстанцирования, нет утилизации, просто fire'n'forget. Я думаю, это была моя первая невольная попытка создать сервис-ориентированную архитектуру - множество сервисов без сохранения состояния, которые просто сделали свою работу и ничего больше. Однако по мере роста системы появляются драконы.

Полиморфизм

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

Интерфейс беды

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

тестирование

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

Фостерс сгустки

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

Параметр ползучести

Начнем с того, что этот маленький симпатичный и невинный статический метод может принимать один параметр. По мере роста функциональности добавляется пара новых параметров. Вскоре добавляются дополнительные параметры, которые являются необязательными, поэтому мы создаем перегрузки метода (или просто добавляем значения по умолчанию на языках, которые их поддерживают). Вскоре у нас есть метод, который принимает 10 параметров. Только первые три действительно необходимы, параметры 4-7 являются необязательными. Но если указан параметр 6, необходимо заполнить 7-9 ... Если бы мы создали класс с единственной целью сделать то, что сделал этот статический метод, мы могли бы решить эту проблему, приняв необходимые параметры в конструктор и позволяющий пользователю устанавливать необязательные значения через свойства или методы для одновременного задания нескольких взаимозависимых значений. Также,

Требовательные потребители для создания экземпляра классов без причины

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

Только ситхи имеют дело с абсолютами

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

Стандарты, стандарты, стандарты!

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

стог
источник
12
Хммм, не знаю, что я бы скопировал и вставил здесь весь ответ. Может быть, ссылка и перефразировать, включить альтернативную точку зрения, так как вы упоминаете «обсуждение», поделиться своим собственным опытом?
Корбин, март,
2
«Только ситхи имеют дело с абсолютами» - я не видел такого раньше ... но это ИДЕАЛЬНО. Сделал меня лол.
Брук
4
@ Корбин: Марк был отличный ответ. Я дал ему кредит, и я скопировал / вставил для простоты ссылки. Мои собственные взгляды по этому вопросу совпадают с мнением Марка. Я не вижу смысла в вашем комментарии, кроме как начать дебаты.
Рик
1
@Rick: Скопированный ответ довольно длинный, и его перефразировка определенно поможет. Просто предложение типа «Дело не только в том, что они не очень полезны, они могут даже навредить, как показано в этом посте ТАК:», позволит мне быстро пропустить это, поскольку я вижу, что это не та информация, которую я искал.
blubb
@Simon: LOL Я 1 + редактировал ваш пост. В попытке прекратить спорить из-за такой глупости, я просто соглашусь. :) Надеюсь, оригинальный постер нашел мой ответ / скопировать и вставить полезным.
Рик
16

Я удивлен, что никто не упомянул, что staticклассы допускают методы расширения - безопасное расширение существующего типа (включая добавление определений методов к интерфейсам ), которым вы не владеете. Например, что в Scala

trait MyFoo {
  def foo: Int
  def plusFoo(a: Int) = foo + a
}

может быть выражено в C # как

public interface IMyFoo {
  int Foo();
}

public static class MyFooExtensions {
  public static int PlusFoo(this IMyFoo f, int a) {
    return f.Foo() + a;
  }
}
Фрэнк Шиарар
источник
ты нашел иголку в стоге сена. +1
Саид Нимати
3

Для меня статический класс (в C #) подобен вызову функции.

Например

public static string DoSomething(string DoSomethingWithThisString){}

Я передаю строку и получаю обратно строку. Все содержится в этом методе. Нет доступа к переменной-члену и т. Д.

Честно говоря, в основном он использовался для уменьшения количества строк кода:

Почему:

MyClass class = new MyClass();
String s = class.DoSomething("test");

Когда я могу сделать это:

String s = MyClass.DoSomething("test");

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

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

Это может быть упрощенно, но в основном я так и использовал static.

Джон Рейнор
источник
1
new MyClass (). DoSomething ("test") по-прежнему действует. Я часто использую это для тестовых сборщиков данных.
JoanComasFdz