Причини неінтуїтивної реалізації 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 , але, якщо чесно, я дійсно думав, що десь є підвищення продуктивності. Я думаю, я помилявся


3
Я теж думав про це. Думаю, що вони просто не доклали великих зусиль для розробки цього єдиного API. І якщо вони зрозуміли свою помилку, було б пізно.
Ейфорія

@Caleth Ви можете детальніше розглянути це. Можливо, я помиляюся, але не бачу, що в цьому дивно. Чому я не можу робити, testString.Split(",.;");і testString.Split(new Char [] {',', '.', ';',);це не те саме.
scharette

@Euphoric Я теж так бився, але це було б так дивно. Сподіваюся, хтось прийде з більш логічною відповіддю.
scharette

Ви можете перебирати рядок так само, як і IEnumerable<char>такий додатковий прототип, який ви пропонуєте, у певних випадках може здаватися неоднозначним (ви розмежовуєте їх цілим рядком чи розмежуєте кожного його символу?) Просто здогадка.
Джон Ву

@JohnWu Можливо, це особиста річ, але для 99,9% випадків подібних синтаксисів testString.Split("anotherString");я досить впевнено стверджую, що очікувана поведінка полягала в тому, щоб розмежувати весь рядок ( anotherStringв даному випадку).
scharette

Відповіді:


15

Іноді поділ на декілька char / string є корисним, тому 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.


1
Ну, це взагалі корисно? Сумніваєтесь у цьому. І це лише перерва API, а не ABI.
Дедуплікатор

2
@Deduplicator: Спліт (null) розпадається на пробіли, тому це, мабуть, один із найпоширеніших випадків використання для розділення, хоча поганий дизайн API використовувати такий null, як цей.
ЖакБ

1
Я думаю, що @Deduplicator хотів сказати, що Split(null)це марно, якщо ти дозволяєш Split(""). Окрім того, що це дозволило б краще покращити синтаксис, останній у будь-якому разі є більш багатослівним ...
scharette

1
@scharette: Звичайно, але змінити це неможливо, не порушуючи зворотної сумісності.
ЖакБ

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

Якщо ви не намагаєтесь дебатувати до речі, ваша думка цілком справедлива. Просто намагаюся зрозуміти причину цього. Логічно має бути історична причина чи причина…
scharette

@scharette: Причина полягає в тому, щоб зробити метод максимально загальним призначенням. Наскільки краще, коли ви знайдете підпис обраного методу, він не працюватиме для кількох роздільників. Версія Microsoft буде працювати для декількох роздільників, а також для вашого окремого роздільника.
Роберт Харві

@RobertHarvey Добре, чи не було б обом можливим? Скажімо, метод розширення у наведеній вище відповіді був частиною Stringкласу, і те й інше було б можливим. Я помиляюся ?
scharette

Я думаю, ви пропускаєте суть. Ваше перевантаження дозволяє лише один роздільник. Перевантаження Microsoft дозволяє більше одного. Ви не можете викликати свою перевантаження кілька разів і домогтися того ж результату; це не так, як це працює.
Роберт Харві

1

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

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

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


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