Причини неінтуїтивної реалізації 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.