Accueil

C# Le mot clef dynamic

by Jean-Camille Mercier 1. septembre 2013 19:43

Voilà déjà quelques années que le mot clef "dynamic" a été inventé, c'était au lancement du C# 4.0 avec Visual Studio 2010 (rappelons que nous sommes actuellement en C# 5.0 avec le framework 4.5 dans l'edi 2012). Mais franchement, avoir l'utilité concrète d'une variable qui change de type, c'est vraiment rare ... la plupart du temps on va plutôt choisir de travailler avec une interface, créer plusieurs variables ou même utiliser un simple "object" qui fera largement l'affaire !

Mais aujourd'hui je suis tombé sur LE cas d'utilisation qui m'a permis d'utiliser un dynamic pour la première fois de ma (longue) vie de développeur !

C'est un cas plutôt complexe bien sur qui mêle de la généricité et la reflexion, j'ai une ligne de texte qui représente un objet que je ne connais pas, il faut découper cette ligne et affecter les valeurs trouvées dans les propriétés de cet objet en respectant l'ordre et le type de chaque propriété.

private static T DeserializeLigne<T>(string ligne) where T : class, new()
{
    Type ligneType = typeof(T);
    T retval = new T();

    // Recherhe de toutes les propriétés de l'objet
    PropertyInfo[] membres = ligneType.GetProperties();

    // On cherche celle qui ont un ChampDescriptionAttribute
    var colonnes = membres
        .Select(m => new
        {
            Membre = m,
            Desc = m.GetCustomAttribute<ChampDescriptionAttribute>()
        })
        .Where(n => n.Desc != null)
        // Attention a bien trier par l'ordre d'apparition dans le fichier
        .OrderBy(m => m.Desc.Ordre)
        .ToList();

    // Vérif de la longueur entre la description et la ligne réelle
    int longueurTheorique = colonnes.Sum(c => c.Desc.Taille);
    if (ligne.Length != longueurTheorique)
        throw new ApplicationException("Longueur de chaine incohérente");

    int position = 0;
    // Pour chaque colonne, découpage de la ligne, conversion et affectation de la valeur
    foreach (var colonne in colonnes)
    {
        // Découpage
        dynamic value = ligne.Substring(position, colonne.Desc.Taille);
        // Avancer dans la ligne
        position += colonne.Desc.Taille;
        // Verif si vide
        if (string.IsNullOrWhiteSpace(value))
            continue;
        // Conversion 
        switch (colonne.Desc.DataType)
        {
            case FubeDataTypeEnum.AlphaNumerique:
                value = value.Trim();
                break;
            case FubeDataTypeEnum.Numerique:
                value = Convert.ToInt32(value);
                break;
            case FubeDataTypeEnum.Decimal:
                // Toujours 2 chiffres après la virgule
                value = value.Insert(value.Length - 2, ".");
                value = Decimal.Parse(value, CultureInfo.InvariantCulture);
                break;
            case FubeDataTypeEnum.Date:
                // Format AAMMJJ
                value = DateTime.ParseExact(value, "yyMMdd", null);
                break;
        }
        // Affectation
        colonne.Membre.SetValue(retval, value);
    }

    return retval;
}

Le dynamic arrive à la fin, je connais la valeur de la ligne, j'ai trouvé la propriété (et donc son type) et je dois donc affecter cette valeur typée à ma propriété en reflexion sachant que le SetValue ne fonctionnera pas si la valeur n'est pas le bon type ! Donc mon pointeur est dans tous les cas un string au début, et devient soit un int, soit un décimal, soit une date. Je peux ensuite affecter mon dynamic sans crainte à mon objet.

Sans le dynamic, il aurait fallut créer une variable dans chaque case de mon switch et faire une affectation différente dans chaque portée : moins classe :)