В чем смысл статичности определенных методов для типов данных?
В C #, например, есть статические методы для определения того, является ли строка нулевой или пустой , поиска элемента в массиве, очистки массива и т. Д. Однако существует метод экземпляра для замены символов в строке, причем как статический, так и экземплярный. методы копирования элементов между массивами.
Точно так же в Python есть функции для получения длины списка, для фильтрации списка и т. Д., Но методы экземпляра для поиска индекса элемента и для копирования списка.
Напротив, в JavaScript практически любая операция, которую мне нужно выполнить со встроенным типом данных, будет доступна через метод или свойство экземпляра.
Так в чем же смысл делать одни вещи статичными, а другие нет?
РЕДАКТИРОВАТЬ : Чтобы быть ясным, я понимаю цель статических методов. Мой вопрос больше касается того, почему определенные методы написаны как статические, хотя их можно было бы написать как методы экземпляра. Как Find
для массивов, так и Format
для строк в C # filter
и len
в Python.
Ответы
РЕДАКТИРОВАТЬ : вопрос был уточнен, поэтому я уточню свой ответ, но оставлю предыдущий ниже, чтобы комментарии не были неуместными.
Может быть несколько причин для реализации метода как статического метода вместо метода экземпляра, с разными языками или фреймворками, возможно, имеющими разные обоснования:
Открытый интерфейс объекта - это контракт. Если ваш
Array
тип предоставляетSort
метод, каждый массив с этого момента, независимо от типа или версии языка, должен его поддерживать. Если, однако, этоArray
просто представление данных в памяти с тонким контрактом, а логика перемещена в такой классArray.Sort
, это дает вам больше гибкости для расширения или изменения вашего поведения, не нарушая совместимости.См. Эту цитату из Чистого кода Боба Мартина :
Объекты раскрывают поведение и скрывают данные; Структура данных предоставляет данные и не имеет существенного поведения "
В этом случае List
класс - это класс, который демонстрирует поведение - он скрывает свои данные (он реализован как массив с изменяемым размером, но это деталь реализации), но предоставляет функциональные возможности. int[]
, Однако, представляет собой структуру данных. Это последовательно размещенный int
блок. У него есть Length
свойство, потому что оно является частью его данных , но не имеет особого поведения - поведение передается классу, который с ним работает.
- Это может быть стилистический выбор. Python не является чисто объектно-ориентированным языком и может не иметь гарантии Java / C # «все является объектом», поэтому он решил использовать такие языковые функции,
len()
которые можно использовать независимо от того, является ли его параметр экземпляром объекта или нет.
Предыдущий ответ
Смысл статических методов в C # состоит в том, чтобы иметь методы, которые привязаны к типу, но не к экземпляру . Причин тому может быть несколько:
- Методы, которые могут работать как с экземпляром
null
, так иstring.IsNullOrEmpty
, естественно, не могут быть методами экземпляра - потому что экземпляра может не быть. - Аналогичным образом фабричные методы, создающие экземпляр, не имеют экземпляра для вызова. Например, устаревший статический
WebRequest.Create()
метод возвращает новый экземпляр типа, производного отWebRequest
. - Хотя об этом легко забыть, но .NET по-разному обрабатывает собственный интегральный тип (
int i =5
) и ссылочный тип в рамке вокруг него (object boxed = 5
). Если вы вызываете метод для типа значения (int i = 5; t.ToString();
), вы платите за его упаковку. Вот почему некоторые операции определены не как метод объекта, а как статический метод, который принимает значение. Вот почему математические операции, такиеMath.Abs
как не определены как методы для упакованных типов, а как статические функции, которые получают и возвращают значения. - Иногда статические методы используются для «настройки» типа, но не одного конкретного экземпляра, а поведения этого типа в системе. Например,
[ServicePointManager.SetTcpKeepAlive()][1]
настраивает поведение всех классов, которые используют общий стек HTTP (ов) в .NET, поэтому вы можете настроить их все с помощью этих статических методов.
Статические методы имеют большой смысл
- когда метод связан с типом, но не требует существующего экземпляра; или
- когда язык поддерживает пространство имен только через классы.
C # поддерживает функции только как члены классов, поэтому вспомогательные функции должны быть написаны как статические методы. Класс C # Math
- яркий тому пример.
В JavaScript нет классов, но каждая нелокальная переменная связана с некоторым объектом - в браузере большинство API-интерфейсов доступны через window
объект. Однако есть методы, доступные через объект, подобный классу, например Array.isArray(x)
.
Python имеет своеобразный дизайн, который в основном объектно-ориентированный, но имеет некоторые встроенные функции, такие как len()
или iter()
, в основном по историческим причинам. Под капотом они вызывают реальные методы, например, iter(x)
как правило x.__iter__()
, с резервным вариантом для объектов, которые могут быть проиндексированы как список.
Более интересны методы, которые созданы static
не из-за особенностей языка, а потому, что они имеют смысл. Конструкторские или фабричные методы - классический пример, когда у нас нет существующего объекта, для которого мы могли бы вызвать метод. Некоторые функции также не работают точно с одним объектом, например, String.IsNullOrEmpty(x)
работают с нулем или одним объектом. Это не работает как метод, например, x.IsNullOrEmpty()
вызовет исключение, если оно равно нулю! Array.isArray(x)
Метод JS еще один пример: мы хотим вызвать isArray(x)
на объектах любого типа, а не только на массивах. Таким образом, метод экземпляра не работает, если он не является частью прототипа каждого объекта.