Partager via


Sondage: string ou collection ?

Tous les jours, nos programmes utilisent de nombreuses variables de tout type. Parmi elles, les chaines de caractères y sont massivement présentes. Ce besoin évident de stocker du texte cause cependant de nombreux problèmes depuis toujours.

En effet, on attend d'une chaine de caractère de pouvoir lui concaténer une autre chaine, de la réduire en supprimant certains caractères, de la passer en majuscule, etc.

Toutes ces opérations changent la taille mémoire de la chaine et posent problème car on s'aperçoit bel et bien que l'on a à faire à un tableau (ou buffer) et non pas à un type simple.

Le framework .Net a donc pris le choix de considérer le type string comme immuable. Qu'est-ce donc ?

Celà veut dire qu'une fois une chaine allouée, on ne peut plus la modifier. Toute opération créera donc une autre chaine de caractères.

Nous savons qu'il n'est pas recommandé de concaténer plusieurs chaines de caractère de suite. En effet l'opération suivante, bien que d'apparence banale est très consommatrice en mémoire.

string s = "Bonjour Mr " + nom + " " + prenom + ".";

Chaque concaténation va créer et allouer autant de fois que nécessaire de nouvelles chaines de caractère alors que notre finalité est d'en créer une seule.
La classe StringBuilder ou encore la méthode string.Format() sont alors beaucoup plus efficaces.

Ce rappel sur les chaines est toujours intéressant mais ce n'est qu'une introduction. C# 3.0 et la technologie Linq se basent fortement sur les énumérations. Les tableaux .Net sont des énumérations typées. Ainsi, la classe string, comme tout tableau, implémente IEnumerable<Char>.

On peut trouver beaucoup d'avantages à voir une chaine comme étant une énumération.

Par exemple, s.Skip(10).Take(5).Skip(10) n'alloue aucune chaine intermédiaire contrairement à: s.Substring(10, 5) + s.Substring(10 + 5 + 10,  s.Length - (10+5+10)).

Donc tant que l'on appelle des méthodes Linq en séquence, on ne fait que jouer avec des énumérations sans créer de collection intermédiaire. (cf Pourquoi préférer les itérations aux collections)

L'avantage est considérable mais un problème subsiste: le résultat n'est pas une chaine mais IEnumerable<Char>. Comment revenir à une simple chaine ?

Revenir d'une énumération de caractères à une chaine les concaténant tous revient finalement à faire une aggrégation sur la série comme on le ferait en calculant la somme d'une liste d'entiers.

Ainsi il est possible d'appeler:

string result = "";
s.Skip(10).Aggregate(result, (r, c) => r = r + c);

Attention car nous retombons sur le problème initial de concaténation de chaine. Nous pouvons le résoudre avec la même solution:

StringBuilder result = new StringBuilder();
s.Skip(10).Aggregate(result, (r, c) => r.Append(c));

Voici un petit exemple mettant en oeuvre cette technique. L'idée est d'implémenter une méthode renvoyant la liste des mots contenus dans une chaine de caractères.

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace TestConstructorParameter
{
    public static class MyStringExtentions
    {
        public static IEnumerable<string> ToWords(this IEnumerable<char> source)
        {
            StringBuilder wordBuilder = new StringBuilder();

            foreach (char c in source)
            {
                if (c != ' ')
                    wordBuilder.Append(c);
                else
                {
                    if (wordBuilder.Length > 0)
                    {
                        yield return wordBuilder.ToString();
                        wordBuilder = new StringBuilder();
                    }
                }
            }
            if (wordBuilder.Length > 0)
                yield return wordBuilder.ToString();
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            string source = "Bonjour tout le monde";

            foreach (string s in source.ToWords())
                Console.WriteLine(s);
    }
}

Allez pour finir, un petit sondage. L'intellisense des méthodes d'extension de Linq a été retiré de Visual Studio 2008 Beta 2. Lors des précédentes versions, l'équipe C# a reçu beaucoup de retours de développeurs trouvant troublant de considérer les chaines de caractères comme des énumérations de char et de voir ainsi les méthodes de Linq cotoyer les méthodes classiques substring, split et autres.
...depuis, ils ont des retours demandant pourquoi diable l'intellisense avait-elle disparu :-).

A vous de voter en répondant à ce post !

Mitsu

Comments

  • Anonymous
    September 26, 2007
    PingBack from http://www.artofbam.com/wordpress/?p=3157

  • Anonymous
    September 26, 2007
    Merci pour ce billet que je trouve très intéressant :-). ( Je me suis dis la même chose pour l'intelliense des méthodes d'extension :D! )

  • Anonymous
    September 28, 2007
    J'essaie justement d'optimiser un parser texte. Je suis entrain de faire des tests avec Linq (plus pour le fun pour le moment). Merci pour l'article !

  • Anonymous
    September 30, 2007
    Merci pour les commentaires, mais n'oubliez pas le sondage !! Alors, intellisense sur les méthodes d'extension sur la classe string dans VS2008 RTM ou pas ???

  • Anonymous
    September 30, 2007
    Pour l'intellisence. Je trouve assez surprenant de pouvoir faire quelque chose et que l'intellisence ne nous le propose pas. Cependant si des personnes sont contre, on peut éventuellement proposer une solution intermédiaire en ne faisant proposé à l'intellisence que le AsEnumerable(). Mais bon, personnellement, je préfère vraiment avoir tout.

  • Anonymous
    October 01, 2007
    Je suis pour ! une chaine n'est rien d'autre qu'une collection de char...autant le voir dans l'intellisense :-) d'autant que cela compile !

  • Anonymous
    October 01, 2007
    Plutôt d'accord avec les avis précédents. Qui peut le plus peut le moins, je ne vois donc pas pourquoi il faudrait "masquer" certaines méthodes dans l'intellisense.

  • Anonymous
    October 01, 2007
    A mon avis, il serait plus logique de le proposer dans l'intellisense, par contre une option pour désactiver les extensions methods de l'intellisense pourrait mettre tout le monde d'accord !

  • Anonymous
    October 02, 2007
    Peut-être qu'il faut des standart/advanced pour l'intellisence. Il y a déjà des trucs comme cela: la configuration "Just My Code" est très pratique pour simplifier la call-stack. Même si des fois, il nous faut voir toute la stack.

  • Anonymous
    October 04, 2007
    Plutot pour (l'intelliSense etant la pour aider et eviter de tout retenir "par coeur") peut etre en differenciant les methodes d'extensions des autres methodes avec un icone particulier par exemple ..

  • Anonymous
    October 24, 2007
    définitivement Pour ! Mais ne pourrait-on pas avoir une petite marque distinctive précisant la catégorie à laquelle appartient la méthode ?

  • Anonymous
    November 29, 2007
    The comment has been removed

  • Anonymous
    August 18, 2008
    Réponse au quizz précédent. Ce quizz va me permettre de rappeler plusieurs points intéressants: - Jouer