Raisons derrière l'implémentation C # String.Split () non intuitive

10

En C # si je veux diviser un stringpar un autre, stringje dois faire quelque chose comme ça:

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

De la String.Splitdocumentation MSDN surchargée, nous pouvons voir la mise en œuvre et pourquoi un tel appel doit être effectué.

Venant de Python , il m'est difficile de comprendre correctement pourquoi un tel appel est nécessaire. Je veux dire que je pourrais utiliser Regex.Splitpour obtenir une syntaxe similaire à celle de l'implémentation de Python, mais je devrais le faire au prix de moins de performances (temps de configuration) pour quelque chose de simple .

Donc, fondamentalement, ma question est pourquoi diable ne pouvons-nous pas simplement faire:

testString.Split("anotherString");

Notez que je ne suggère aucun prototype ni implémentation. Je comprends pourquoi vous n'avez pas pu implémenter la version ci-dessus compte tenu de l'API actuelle. Mon objectif était de comprendre pourquoi une telle API aurait pu être créée compte tenu de l'avantage que la syntaxe ci-dessus apporte. À l'heure actuelle, la flexibilité semble être l'objectif du courant String.Splitqui a du sens, mais pour être honnête, je pensais vraiment qu'il y avait une sorte de gain de performances quelque part. Je suppose que j'avais tort.

scharette
la source
3
J'y pensais aussi. Ma spéculation est qu'ils n'ont tout simplement pas mis beaucoup d'efforts dans la conception de cette API. Et s'ils ont réalisé leur erreur, il était trop tard.
Euphoric
@Caleth Pouvez-vous nous en dire plus. peut-être que je me trompe mais je ne vois pas ce qui est ambigu à ce sujet. Pourquoi ne puis-je pas le faire testString.Split(",.;");et testString.Split(new Char [] {',', '.', ';',);qui ne sont pas la même chose.
scharette
@ Euphorique, je le pensais aussi, mais ce serait tellement étrange. J'espère que quelqu'un viendra avec une réponse plus logique.
scharette
Vous pouvez effectuer une itération sur une chaîne comme un IEnumerable<char>afin que le prototype supplémentaire que vous proposez puisse apparaître ambigu dans certains cas (délimitez-vous par la chaîne entière ou par chacun de ses caractères?) Juste une supposition.
John Wu
@JohnWu Peut-être que c'est une chose personnelle, mais pour 99,9% des occurrences de syntaxe comme testString.Split("anotherString");, je suis assez confiant pour dire que le comportement attendu était de délimiter sur toute la chaîne ( anotherStringdans ce cas).
scharette

Réponses:

15

Parfois, le fractionnement sur plusieurs caractères / chaînes est utile, de sorte que l'API vous permet de fournir un tableau, vous offrant ainsi une flexibilité maximale. Dans le cas de chars, vous obtenez à la fois la simplicité de la syntaxe et la flexibilité puisque le paramètre est marqué comme paramssi vous pouvez écrire Split('x')plutôt que Split(new[]{'x'}).

Alors pourquoi n'y a-t-il pas une option similaire pour les chaînes, vous permettant d'écrire Split("x")?

C'est peut-être une conséquence malheureuse de la façon dont l'API est conçue. Initialement, cela ne permettait que le fractionnement des caractères. Le fractionnement sur les chaînes a été ajouté en 2.0, probablement parce qu'il est plus complexe à implémenter. Mais il n'était pas possible d'ajouter String.Split(string)ou de String.Split(string[])surcharger, car cela rendrait l'expression testString.Split(null)ambiguë et ce code ne compilerait plus.

testString.Split(null) est en fait un idiome assez courant car il fractionne la chaîne sur les espaces, donc une telle rupture serait trop répandue pour être acceptable.

L'utilisation d'un nullparamètre comme commutateur pour un comportement spécial est généralement considérée comme une mauvaise conception de nos jours, donc je pense qu'il est juste de dire que cette API est juste imparfaite.

Il n'y en a pas non Split(string[], Int32)plus, probablement pour une raison similaire - ce serait ambigu Split(char[], Int32)si le premier paramètre l'est null. Il y a des surcharges similaires avec les StringSplitOptionsparamètres, mais ils ont tous été ajoutés en même temps dans 2.0, donc aucune ambiguïté n'a été introduite dans le code existant.

Remarque

Pour être clair, ceci est juste mon hypothèse, je ne connais pas la pensée réelle des concepteurs de framework .net.

JacquesB
la source
1
Eh bien, est-ce vraiment utile? J'en doute. Et ce n'est qu'une rupture API, pas une rupture ABI.
Déduplicateur
2
@Deduplicator: Split (null) se divise sur les espaces, il s'agit donc probablement d'un des cas d'utilisation les plus courants pour le split, même si la conception de l'API est mauvaise pour utiliser un null comme celui-ci.
JacquesB
1
Je pense que @Deduplicator voulait dire que cela Split(null)ne sert à rien si vous le permettez Split(""). Outre le fait qu'elle permettrait une bien meilleure syntaxe, cette dernière est quand même plus verbeuse ...
scharette
1
@scharette: Bien sûr, mais il n'est pas possible de changer maintenant, sans briser la compatibilité descendante.
JacquesB
1
une note: avec l'aperçu actuel de C # 8, en désactivant les types de base, la nullité String.Split(null)ne serait plus ambiguë, afin qu'ils puissent ajouter la surcharge
BgrWorker
2

N'étant pas l'auteur des méthodes, je ne sais pas pourquoi cet ensemble de surcharges a été choisi. Cependant, il y a deux choses à noter ici:

  1. Si vous divisez un seul caractère, la public string[] Split(params char[] separatorversion) peut être utilisée ainsi:

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

    comme char[]est un paramsparamètre.

  2. Vous pouvez facilement ajouter votre propre méthode d'extension ici pour obtenir ce que vous voulez:

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

    et testString.Split("anotherString");va maintenant travailler pour vous.

David Arno
la source
1
Merci pour les commentaires. Bien que votre réponse soit utile et concise, je ne suis pas d'accord avec vous. Surtout le deuxième point. N'est-ce pas une raison de plus pour l'intégrer? Tout ce qu'il fait est de laisser la communauté créer une version différente d'une méthode que tout le monde (ou presque tout le monde) attend de se comporter de la même manière.
scharette
N'essayant pas de débattre en passant, votre argument est tout à fait valable. J'essaie juste de comprendre la raison derrière cela. Logiquement il doit y avoir une raison historique ou de performance ...
scharette
@scharette: La raison est de rendre la méthode aussi polyvalente que possible. Aussi préférable que vous trouviez la signature de votre méthode choisie, elle ne fonctionnera pas pour plusieurs délimiteurs. La version de Microsoft fonctionnera pour plusieurs délimiteurs ainsi que pour votre seul délimiteur.
Robert Harvey
@RobertHarvey Eh bien, les deux ne seraient pas possibles? Disons que la méthode d'extension dans la réponse ci-dessus faisait partie de la Stringclasse, les deux seraient possibles. Ai-je tort ?
scharette
Je pense que vous manquez le point. Votre surcharge ne permet qu'un seul délimiteur. La surcharge de Microsoft en autorise plusieurs. Vous ne pouvez pas appeler votre surcharge plusieurs fois et obtenir le même résultat; ce n'est pas comme ça que ça marche.
Robert Harvey
1

Différentes langues ont des règles quelque peu différentes pour les conversions implicites et la surcharge, et le .NET Framework est conçu pour être utilisable avec n'importe lequel d'entre eux. Dans le Option Strict Offdialecte de VB.NET, une valeur de type Stringpeut être transmise à une fonction qui attend un Char[]comportement équivalent à l'appel ToCharArray()à la chaîne.

Je pense que la chose raisonnable à faire aurait été d'avoir des noms séparés pour Split(qui accepte un seul Charou String) et SplitMulti(qui accepterait un Char[]ou String[]), mais .NET semble parfois favoriser l'utilisation de la surcharge seule pour choisir différents types d'opérations. Malheureusement, je ne connais aucun moyen d'utiliser String.Splitpour s'adapter à des scénarios d'utilisation qui nécessiteraient de distinguer différents types de délimiteurs autrement qu'en les séparant séparément.

Une autre omission est une option pour conserver les délimiteurs, soit en les incluant à la fin de la chaîne précédente, soit au début de la chaîne suivante, ou en ayant des éléments de tableau impairs comme délimiteurs tandis que les éléments pairs sont les choses entre eux.

supercat
la source
1
.NET semble parfois favoriser l'utilisation de la surcharge seule pour choisir différents types d'opérations. Tellement vrai ...
scharette