Чому і коли я повинен робити клас "статичним"? Яка мета "статичного" ключового слова на класах?


47

staticКлючове слово в якості члена в багатьох мовах означає , що ви не повинні створити екземпляр цього класу , щоб мати можливість мати доступ до такого Члена СОТ. Однак я не бачу жодного обгрунтування для створення цілого класу static. Чому і коли я повинен робити клас static?

Які переваги я отримую від заняття static? Я маю на увазі, після оголошення статичного класу все-таки слід оголосити всіх членів, до яких він хоче мати доступ, без примірників, як статичних.

Це означає, що, наприклад, Mathклас можна оголосити нормальним (не статичним), не впливаючи на те, як розробники кодують. Іншими словами, зробити клас статичним або нормальним - це начебто прозоре для розробників.


Якщо ви кодуєте у стилі Func Prog a la Rich Hickey, це може бути корисним.
робота

1
На мою думку, статичні класи - це просто милиця, за допомогою якої C # намагається приховати величезний недолік дизайну в мові. Навіщо структурувати мову навколо цієї безглуздої ідеології все, що має бути, лише для того, щоб потім ввести додатковий синтаксис, щоб ви могли обманювати і працювати над цим непотрібним обмеженням. Якби C # тільки що дозволив вільні функції з початку роботи, статичні класи не потребували б.
антред

Відповіді:


32

Це дає зрозуміти користувачам, як використовується клас. Наприклад, було б повною дурницею написати наступний код:

Math m = new Math();

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


3
Я не згоден з цією філософією. Проблема, яка в мене є, потребує змін. Тож, мабуть, було доцільним обмежувати його до цього часу, але обмеження перешкоджає нашому розвитку для майбутніх потреб. І застаріла бібліотека, яку ми змушені використовувати для взаємодії зі старою системою (тієї, яку ви написали та запечатали та зафіксували всі класи), не дає нам жодного способу розширити чи змінити той код, який ви написали. Тільки те, що у вас не було причин призначати свій клас, не означає, що я не матиму потреби в цьому в майбутньому. Хоча класифікація класу корисності має сенс, враховуйте і майбутнє.
SoylentGray

19
@Chad Тоді бібліотека погано розроблена. Розширення класів, які не розраховані на розширення, в кінцевому рахунку не працює, тому найкраще зробіть клас sealedна першому місці. Я усвідомлюю, що не всі люди згодні з цим почуттям, але коментарі - не вдале місце для обговорення цього питання. Але в разі staticцієї проблеми навіть не ставить себе: інстанцірованія System.MathНЕ буде ніколи мати сенс.
Конрад Рудольф

1
@Konard, якщо ви оголосите всіх членів Mathкласу статичними, не оголошуючи Mathклас статичним, все одно ви можете використовувати його, не потребуючи примірників.
Саїд Неаматі

11
@Saeed - Це правда, але ви також можете створити клас, який не відповідає жодним цілям. Що робити з екземпляром класу Math? Щоб явно показати наміри класу (не потрібні і не потрібні інстанції), він позначений static.
Корбін березня

3
@Saeed Давайте розвернемо це: я пропоную вам ще раз прочитати мою відповідь, оскільки, здається, ви її неправильно зрозуміли: Ваші коментарі не пов'язані з моєю відповіддю. Я ніколи не говорив, що потрібно писати new Math().
Конрад Рудольф

24

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

Я писав свої думки про статичні класи в попередній темі :

Я любив класні програми, наповнені статичними методами. Вони зробили велику консолідацію допоміжних методів, які в іншому випадку могли б лежати, викликаючи надмірність та технічне обслуговування. Вони дуже прості у використанні, без встановлення інстанцій, без утилізації, просто забудьте. Я здогадуюсь, це була моя перша мимовільна спроба створити архітектуру, орієнтовану на сервіс - безліч служб без громадянства, які просто зробили свою роботу і нічого іншого. У міру зростання системи, але дракони приходять.

Поліморфізм

Скажімо, у нас є метод UtilityClass.SomeMethod, який радісно гуде разом. Раптом нам потрібно трохи змінити функціональність. Більшість функціоналів однакові, але нам все-таки потрібно змінити пару деталей. Якби це не був статичний метод, ми могли б зробити клас похідної та змінити вміст методу за потребою. Оскільки це статичний метод, ми не можемо. Звичайно, якщо нам просто потрібно додати функціональність перед або після старого методу, ми можемо створити новий клас і викликати старий всередині нього - але це просто грубо.

Інтерфейсні неприємності

Статичні методи не можуть бути визначені через інтерфейси з логічних причин. А оскільки ми не можемо перекрити статичні методи, статичні класи марні, коли нам потрібно передати їх по інтерфейсу. Це не дає нам можливості використовувати статичні класи як частину стратегії. Ми можемо виправити деякі проблеми, передаючи делегатів замість інтерфейсів.

Тестування

Це, в основному, йде рука об руку із згаданими вище неприємностями інтерфейсу. Оскільки наша можливість взаємозамінних реалізацій дуже обмежена, ми також матимемо проблеми із заміною виробничого коду тестовим кодом. Знову ж таки, ми можемо їх завершити, але це вимагатиме від нас змінити велику частину нашого коду лише для того, щоб мати змогу приймати обгортки замість фактичних об'єктів.

Виховує краплі

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

Повзання параметра

Для початку цей маленький милий і невинний статичний метод може взяти єдиний параметр. У міру зростання функціональності додається пара нових параметрів. Незабаром додаються додаткові параметри, які є необов'язковими, тому ми створюємо перевантаження методу (або просто додаємо значення за замовчуванням на мовах, які їх підтримують). Невдовзі у нас є метод, який приймає 10 параметрів. Потрібні лише перші три, параметри 4-7 необов’язкові. Але якщо вказано параметр 6, також потрібно заповнити 7-9 ... Якби ми створили клас з єдиною метою робити те, що зробив цей статичний метод, ми могли б вирішити це, взявши потрібні параметри в конструктор і дозволяє користувачеві встановлювати необов'язкові значення за допомогою властивостей або методів встановлювати одночасно декілька взаємозалежних значень. Також,

Вимагаючи від споживачів створити екземпляр класів без причини

Один з найпоширеніших аргументів - чому вимагають, щоб споживачі нашого класу створили екземпляр для виклику цього єдиного методу, не використовуючи при цьому для цього примірника? Створення екземпляра класу - дуже дешева операція на більшості мов, тому швидкість не є проблемою. Додавання додаткового рядка коду до споживача - це низька вартість закладання фундаменту набагато більш досяжного рішення в майбутньому. І нарешті, якщо ви хочете уникати створення екземплярів, просто створіть однотонну обгортку свого класу, яка дозволяє легко повторно використовувати - хоча це вимагає, щоб ваш клас був без громадянства. Якщо це не без громадянства, ви все одно можете створити статичні методи обгортки, які впораються з усім, одночасно надаючи всі переваги в довгостроковій перспективі. Нарешті,

Лише сіт займається абсолютами

Звичайно, є винятки з моєї неприязні до статичних методів. Справжні класи утиліти, які не становлять ніякого ризику розростання, є прекрасними випадками для статичних методів - System.Convert як приклад. Якщо ваш проект є одноразовим і не вимагає подальшого обслуговування, загальна архітектура насправді не дуже важлива - статична або нестатична, насправді не має значення - швидкість розробки, однак, має значення.

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

Використання методів екземплярів не заважає вам також використовувати статичні методи, і навпаки. До тих пір, поки існують міркування щодо диференціації, і це стандартизується. Немає нічого гіршого, ніж переглядати бізнес-шар, розповсюджений різними методами реалізації.


12
Гммм, не знаю, що я скопіював би тут всю відповідь. Можливо, посилання та перефразовування, включити альтернативну точку зору, оскільки ви згадуєте про "дискусію", поділіться власним досвідом?
Корбін березня

2
"Тільки Sith торгує абсолютними" - я раніше цього не бачив, але так досконало. Зробив мене хаха.
Брук

4
@ Корбін: Марк мав чудову відповідь. Я надав йому кредит і копіюю / вклеюю для зручності посилання. Мої власні погляди на цю справу відповідають Маркові. Я не бачу сенсу у вашому коментарі, крім того, щоб розпочати дискусію.
Рік

1
@ Rick: Скопійований відповідь досить довгий, і перефразовуючи це, безумовно, допоможе. Просто речення на кшталт "Це не просто те, що вони не дуже корисні, вони можуть навіть нашкодити, як показує ця публікація ТА:" дозволив би мені швидко пропустити це, оскільки я бачу, що це не та інформація, яку я шукав.
булька

@Simon: LOL I 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
Saeed Neamati

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
новий MyClass (). DoSomething ("тест") все ще діє. Я дуже використовую це для тестових будівельників даних.
JoanComasFdz
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.