Причины неинтуитивной реализации C # String.Split ()

10

В C #, если я хочу разделить stringна другое, stringя должен сделать что-то вроде этого:

testString.Split(new string[] { "anotherString" }, StringSplitOptions.None);

Из перегруженной String.Splitдокументации MSDN мы можем видеть реализацию и почему такой вызов должен быть сделан.

Исходя из Python , мне трудно понять, зачем нужен такой вызов. Я имею в виду, что мог бы использовать Regex.Splitсинтаксис, подобный реализации Python, но мне пришлось бы делать это за счет меньшей производительности (времени установки) для чего-то простого .

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

testString.Split("anotherString");

Обратите внимание, что я не предлагаю ни прототип, ни реализацию. Я понимаю, почему вы не смогли реализовать вышеуказанную версию с учетом текущего API. Моя цель состояла в том, чтобы понять, почему такой API мог быть создан с учетом преимуществ, которые дает приведенный выше синтаксис. На данный момент гибкость, кажется, является целью текущего, String.Splitчто имеет смысл, но, честно говоря, я действительно думал, что где-то был какой-то прирост производительности. Я думаю, я был неправ.

scharette
источник
3
Я тоже думал об этом. Я предполагаю, что они просто не приложили много усилий для разработки этого единого API. И если они поняли свою ошибку, было слишком поздно.
Эйфорическое
@Caleth Можете ли вы уточнить это? может быть, я ошибаюсь, но я не вижу, что в этом неоднозначного. Почему я не могу сделать, testString.Split(",.;");и testString.Split(new Char [] {',', '.', ';',);что не одно и то же.
шарет
@ Эйфорик Я тоже так думал, но это было бы так странно. Надеюсь, кто-то придет с более логичным ответом.
шарет
Вы можете перебирать строку так же, как и, IEnumerable<char>так что дополнительный прототип, который вы предлагаете, в некоторых случаях может показаться неоднозначным (вы разделяете всю строку или разделяете каждым ее символом?) Просто предположение.
Джон Ву
@JohnWu Возможно, это личная вещь, но для 99,9% случаев синтаксиса, подобных testString.Split("anotherString");, я вполне уверен, что ожидаемое поведение заключалось в разделении всей строки ( anotherStringв данном случае).
Шаретт

Ответы:

15

Иногда полезно разделение более чем одного символа / строки, поэтому API позволяет предоставлять массив, обеспечивая максимальную гибкость. В случае chars вы получаете и простоту синтаксиса, и гибкость, так как параметр помечен как paramsтак, что вы можете писать, Split('x')а не Split(new[]{'x'}).

Так почему же нет похожей опции для строк, позволяющей писать Split("x")?

Возможно, это печальное следствие того, как разработан API. Первоначально это позволило только расщепление на символы. Разделение на строки было добавлено в 2.0, вероятно, потому, что его сложнее реализовать. Но было невозможно добавить String.Split(string)или String.Split(string[])перегрузить, так как это сделало бы выражение testString.Split(null)неоднозначным, и этот код больше не компилировался.

testString.Split(null) на самом деле это довольно распространенная идиома, поскольку она разбивает строку на пустое пространство, поэтому такой разрыв будет слишком распространенным, чтобы быть приемлемым.

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

Нет ни Split(string[], Int32)одного, вероятно, по аналогичной причине - было бы неоднозначно, Split(char[], Int32)если первый параметр null. Там являются подобные перегрузки с StringSplitOptionsпараметрами, но все они были добавлены в то же время в 2,0, так что никакой неоднозначности не была введена в существующем коде.

Запись

Чтобы быть ясным, это всего лишь моя гипотеза, я не знаю фактическое мышление дизайнеров. NET Framework.

JacquesB
источник
1
Ну, это вообще полезно? Сомневаюсь что. И это только API-брейк, а не ABI.
Дедупликатор
2
@Deduplicator: Split (null) разделяется на пустое пространство, так что, вероятно, это один из наиболее распространенных вариантов использования для split, даже если использовать API-интерфейс плохо, как этот.
JacquesB
1
Я думаю, что @Deduplicator хотел сказать, что Split(null)это бесполезно, если вы позволите Split(""). Помимо того факта, что это позволило бы улучшить синтаксис, последний все равно более многословен ...
шаретта
1
@scharette: Конечно, но изменить сейчас невозможно, не нарушив обратной совместимости.
JacquesB
1
примечание: с текущим предварительным просмотром C # 8 отключение базовых типов String.Split(null)больше не будет двусмысленным, поэтому они могут добавить перегрузку
BgrWorker
2

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

  1. Если вы разделяете на один символ, то public string[] Split(params char[] separator) версия может использоваться следующим образом:

    var splitValues = testString.Split(',');

    как char[]это paramsпараметр.

  2. Вы можете легко добавить свой собственный метод расширения, чтобы достичь желаемого:

    public static class StringExtensions
    {
        public static string[] Split(this string source, string separator)
            => source.Split(new string[] { separator }, StringSplitOptions.None);
    }
    

    и теперь testString.Split("anotherString");будет работать на тебя.

Дэвид Арно
источник
1
Спасибо за ответ. Хотя ваш ответ полезен и лаконичен, я не могу с вами согласиться. Особенно второй момент. Есть ли еще одна причина, чтобы встроить его? Все, что он делает, - это позволяет сообществу создавать разные версии метода, которые все (или почти все) ожидают, что будут вести себя одинаково.
шарет
Кстати, не пытаясь спорить, ваша точка зрения полностью обоснована. Просто пытаюсь понять причину этого. По логике вещей, должна быть историческая или исполнительная причина ...
шаретта
@scharette: причина в том, чтобы сделать метод как можно более универсальным. Как бы вы ни выбрали сигнатуру выбранного метода, она не будет работать для нескольких разделителей. Версия Microsoft будет работать как для нескольких разделителей, так и для вашего отдельного разделителя.
Роберт Харви
@RobertHarvey Ну, и то и другое невозможно? Допустим, метод расширения в ответе выше был частью Stringкласса, оба были бы возможны. Я ошибся ?
Шарет
Я думаю, что вы упускаете суть. Ваша перегрузка допускает только один разделитель. Перегрузка Microsoft допускает более одного. Вы не можете вызвать вашу перегрузку несколько раз и достичь того же результата; это не так, как это работает.
Роберт Харви
1

Разные языки имеют несколько разные правила для неявных преобразований и перегрузок, и .NET Framework спроектирован так, чтобы их можно было использовать с любым из них. На Option Strict Offдиалекте VB.NET значение типа Stringможет быть передано функции, которая ожидает Char[]поведение с эквивалентом вызова ToCharArray()строки.

Я думаю, что разумно было бы иметь отдельные имена для Split(который принимает одно Charили String) и SplitMulti(который будет принимать Char[]или String[]), но иногда кажется, что .NET предпочитает использовать одну только перегрузку для выбора различных видов операций. К сожалению, я не знаю ни одного способа использования, String.Splitчтобы приспособиться к любым сценариям использования, которые потребовали бы различения различных типов разделителей, кроме разделения по каждому из них.

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

Supercat
источник
1
Иногда кажется, что .NET предпочитает использовать одну перегрузку для выбора различных видов операций. Так верно ...
шаретта