Чому статичні методи не мають змоги бути переборними?


13

У відповідях на це питання загальний консенсус полягав у тому, що статичні методи не повинні бути перекритими (і, отже, статичні функції в C # не можуть бути віртуальними або абстрактними). Однак це не лише у C #; Java також забороняє це, і C ++, схоже, також не подобається. Однак я можу придумати безліч прикладів статичних функцій, які я хотів би перекрити в дочірньому класі (наприклад, заводські методи). Хоча в теорії існують способи їх обійти, жоден з них не є чистим чи простим.

Чому статичні функції не повинні бути переборними?


4
Delphi підтримує методи класу , які досить схожі на статичні методи і можуть бути переохоплені. Їм передається selfвказівник, який вказує на клас, а не на екземпляр класу.
CodesInChaos

Одне визначення статичного - відсутність змін. Мені цікаво, чому ви створили статичну функцію, яку ви хочете змінити.
JeffO

4
@JeffO: Англійське визначення "статичного" абсолютно не має нічого спільного з унікальною семантикою функцій статичних членів.
Гонки легкості по орбіті

5
Ваше питання «чому статичні методи використовують статичний типізовану відправку замість однієї віртуальної відправки?» Коли ви формулюєте це питання таким чином, я думаю, що він відповідає сам.
Ерік Ліпперт

1
@EricLippert Питання може бути краще сформульовано як "Чому C # пропонує статичні методи замість методів класу?", Але це все ще дійсне питання.
CodesInChaos

Відповіді:


13

За допомогою статичних методів немає об'єкта, який би забезпечив належний контроль над механізмом переосмислення.

Нормальний механізм віртуального методу клас / екземпляр дозволяє тонко налаштовувати контроль над перевірками наступним чином: кожен реальний об'єкт є екземпляром саме одного класу. Цей клас визначає поведінку переопределень; він завжди отримує першу тріщину у віртуальних методах. Потім він може обрати метод виклику батьків у потрібний час для його реалізації. Кожен батьківський метод також отримує свою чергу, щоб викликати свій батьківський метод. Це призводить до гарного каскаду батьківських викликів, який виконує одне з понять повторного використання коду, для якого орієнтація об'єкта відома. (Тут код базових / суперкласів повторно використовується порівняно складним способом; інше ортогональне поняття повторного використання коду в OOP просто має декілька об'єктів одного класу.)

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

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

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

Поміщення статики було б суттєво хаотичним, і подібні речі були зроблені раніше.

Наприклад, Mac OS System 7 та попередньо використовували механізм виправлення пастки для розширення системи, отримуючи контроль над нанесеними додатками системними викликами перед операційною системою. Ви можете подумати про таблицю патчів системних викликів як масив функціональних покажчиків, подібно до vtable для об'єктів, наприклад, за винятком того, що це була одна глобальна таблиця.

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

Це не означає, що неможливо було б створити механізм переопрацювання статики, але те, що я, мабуть, вважав за краще зробити це - перетворити статичні поля та статичні методи в поля екземплярів та методи екземплярів метакласів, щоб нормальний об'єкт Тоді застосовуватимуться методи орієнтації. Зауважте, що є і такі системи: CSE 341: Класи та метакласи Smalltalk ; Дивіться також: Який еквівалент Smalltalk статики Java?


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

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


Якщо я правильно розумію: справа не в тому, теоретично ви хотіли б чи хотіли б зробити щось подібне, а більше, ніж програмно, реалізація цього була б важкою і не обов'язково корисною?
PixelArtDragon

@ Гаран, дивіться мій додаток.
Ерік Ейдт

1
Я не купую аргумент замовлення; ви отримуєте метод, наданий класом, на який ви назвали метод (або перший суперклас, який забезпечує метод відповідно до вибраної вашою мовою лінеаризації).
варення

1
@PierreArlaud: Але this.instanceMethod()має динамічну роздільну здатність, тож якщо self.staticMethod()вона має однакову роздільну здатність, а саме динамічну, вона більше не буде статичним методом.
Йорг W Міттаг

1
для статичних методів потрібно додати переважну семантику. Гіпотетичні Java + метакласи нічого не потрібно додавати! Ви просто робите об'єкти класів, а статичні методи стають звичайними методами екземплярів. Не потрібно щось додати до мови, оскільки ви використовуєте лише поняття, які вже є: об'єкти, класи та методи екземпляра. Як бонус, ви фактично отримуєте видалення статичних методів з мови! Ви можете додати функцію, видаливши поняття і, таким чином, складність з мови!
Йорг W Міттаг

9

Переопределення залежить від віртуальної відправки: ви використовуєте тип виконання thisпараметра для визначення способу виклику. У статичного методу немає thisпараметрів, тож нема чого відправляти.

Деякі мови, зокрема Delphi та Python, мають область "проміжки", яка дозволяє це: методи класу. Метод класу не є звичайним методом екземпляра, але він також не є статичним; він отримує selfпараметр (кумедно, обидві мови називають thisпараметр self), що є посиланням на сам тип об'єкта, а не на екземпляр цього типу. З цим значенням тепер у вас є тип для віртуального відправлення.

На жаль, ні JVM, ні CLR не мають нічого подібного.


Це мови, які використовують selfкласи, як фактичні, екземплярні об'єкти із перезаписуваними методами - Smalltalk звичайно, Ruby, Dart, Objective C, Self, Python? У порівнянні з мовами, які приймають після C ++, де класи не є першокласними об'єктами, навіть якщо є доступ до відображення?
Джері101

Щоб було зрозуміло, ви хочете сказати, що в Python чи Delphi ви можете перекрити методи класу?
Ерік Ейдт

@ErikEidt У Python майже все перезареєстровано. У Delphi метод класу може бути явно оголошений virtualі потім переопрацьований у нащадковому класі, як і методи екземпляра.
Мейсон Уілер

Отже, ці мови надають якесь поняття метакласів, ні?
Ерік Ейдт

1
@ErikEidt Python має "справжні" метакласи. У Delphi посилання на клас - це особливий тип даних, а не сам клас.
Мейсон Уілер

3

Ви запитаєте

Чому статичні функції не повинні бути переборними?

я запитую

Чому функції, які ви хочете змінити, повинні бути статичними?

Деякі мови змушують вас починати шоу статичним методом. Але після цього ви справді можете вирішити дуже багато проблем без будь-яких статичних методів взагалі.

Деякі люди люблять використовувати статичні методи в будь-який час, коли в об'єкті немає залежності від стану. Дехто любить використовувати статичні методи для побудови інших об'єктів. Деякі люди люблять максимально уникати статичних методів.

Ніхто з цих людей не помиляється.

Якщо вам це потрібно для перезапису, просто припиніть маркувати його статичним. Нічого не вдасться зламати, оскільки навколо вас летить об'єкт без громадянства.


Якщо мова підтримує структури, то типом одиниці може бути структура нульового розміру, яка передається методу логічного введення. Створення цього об'єкта - це деталь реалізації. І якщо ми почнемо говорити про деталі реалізації як про справжню істину, то, я думаю, вам також потрібно врахувати, що в реальній реалізації тип нульового розміру не повинен бути фактично ініційованим (тому що ніяких інструкцій для завантаження не потрібно) його або зберігати).
Теодорос Чатзіґянакікіс

І якщо ви говорите ще більше "за кадром" (наприклад, на виконавчому рівні), то аргументи середовища можна визначити як тип мови, а "реальною" точкою введення програми може бути метод примірника цього типу, діючи на будь-який екземпляр, переданий програмі завантажувачем ОС.
Теодорос Чатзіґянакікіс

Я думаю, це залежить від того, як ти на це дивишся. Наприклад, коли програма запускається, ОС завантажує її в пам'ять, а потім переходить до адреси, де знаходиться індивідуальна реалізація програми бінарної точки входу (наприклад, _start()символ на Linux). У цьому прикладі виконуваний файл є об'єктом (у нього є своя "таблиця відправки" і все), а вхідною точкою є динамічно відправлена ​​операція. Отже, точка входу програми за своєю суттю є віртуальною при погляді ззовні, і її можна легко зробити так, щоб вона виглядала віртуально і при погляді зсередини.
Теодорос Чатзіґянакікіс

1
"Ніщо не зірветься, тому що навколо вас летить об'єкт без громадянства". ++++++
RubberDuck

@TheodorosChatzigiannakis ви зробите вагомий аргумент. якщо ні в чому іншому ви мене не переконали, то ця лінія не надихає думку, яку я задумав. Я справді намагався дати людям дозвіл відмахнутися на деякі звичніші практики, які вони сліпо асоціюють зі статичними методами. Тому я оновив. Думки?
candied_orange

2

Чому статичні методи не мають змоги бути переборними?

Це не питання "слід".

"Переоцінка" означає " динамічно відправляти ". "Статичний метод" означає "статично відправлення". Якщо щось є статичним, це неможливо змінити. Якщо щось можна відмінити, це не статично.

Ваше запитання досить схоже на запитання: "Чому триколісні велосипеди не повинні мати чотири колеса?" Визначення з «триколісного велосипеда» є те , що має три колеса. Якщо це триколісний велосипед, він не може мати чотири колеса, якщо у нього чотири колеса, він не може бути триколісним. Так само визначення "статичного методу" полягає в тому, що він статично відправляється. Якщо це статичний метод, він не може бути динамічно відправлений, якщо він може бути динамічно відправлений, він не може бути статичним методом.

Звичайно, цілком можливо мати методи класу, які можна перекрити. Або ви можете мати мову на зразок Ruby, де класи є об'єктами так само, як і будь-який інший об'єкт, і, таким чином, можна використовувати методи екземпляра, що повністю виключає потребу в методах класу. (У Рубі є лише один вид методів: методи екземпляра. У нього немає методів класу, статичних методів, конструкторів, функцій чи процедур.)


1
"За визначенням" Добре кажучи.
candied_orange

1

таким чином статичні функції в C # не можуть бути віртуальними або абстрактними

У C # ви завжди викликаєте статичні члени за допомогою класу, наприклад BaseClass.StaticMethod(), ні baseObject.StaticMethod(). Тож зрештою, якщо ви отримаєте у ChildClassспадок від BaseClassта childObjectекземпляр ChildClass, ви не зможете викликати свій статичний метод childObject. Вам завжди потрібно буде чітко використовувати реальний клас, тому static virtualпросто не має сенсу.

Що ви можете зробити, це перезначити той самий staticметод у вашому дочірньому класі та використовувати newключове слово.

class BaseClass {
    public static int StaticMethod() { return 1; }
}

class ChildClass {
    public static new int StaticMethod() { return BaseClass.StaticMethod() + 2; }
}

int result;    
var baseObj = new BaseClass();
var childObj = new ChildClass();

result = BaseClass.StaticMethod();
result = baseObj.StaticMethod(); // DOES NOT COMPILE

result = ChildClass.StaticMethod();
result = childObj.StaticMethod(); // DOES NOT COMPILE

Якби можна було зателефонувати baseObject.StaticMethod(), то ваше питання мало б сенс.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.