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