IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Contribuez .NET Discussion :

[Source] Calcul de formule par compilation dynamique


Sujet :

Contribuez .NET

  1. #1
    Inactif  
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Janvier 2007
    Messages
    6 604
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Janvier 2007
    Messages : 6 604
    Points : 13 317
    Points
    13 317
    Par défaut [Source] Calcul de formule par compilation dynamique
    La question du calcul de formules stockées dans une chaine est récurrente sur le forum.

    Je vous propose ici une classe simple, permettant d'évaluer la valeur de formules numériques simples, en utilisant la compilation dynamique via "CodeDom".

    La formule peut utiliser les quatre opérations de "base", le modulo, en fait, l'ensemble des opérateur du langage C# (pour faire court), ainsi que n'importe laquelle des méthodes statiques ou des constantes (pi, e) de la classe Math (sans avoir besoin de préfixer la méthode ou la constante - en cela la classe que je poste ici est plus complète que l'exemple que j'avais posté par ailleurs sur le forum). Les méthodes et constantes sont case-insensitive.

    Il est possible d'ajouter une autre classe de méthodes statiques très simplement dans le membre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    private Type[] _classToParse = { typeof( Math ) };
    De la même façon, des références à des assemblies externes peuvent être ajoutées dans le membre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    private string[] _referencesToAdd =  { "System.DLL" };
    ainsi que les namespaces associés dans

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    private string[] _nameSpacesToAdd =  { "System" };
    Bien entendu, cette classe est un simple squelette, bien insuffisant pour de vrais calculs car elle retourne toujours un simple "double" et ne sait pas travailler sur des vecteurs par exemple.

    Voici donc le code

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.CodeDom.Compiler;
    using System.Reflection;
    using System.CodeDom;
    using System.Collections.ObjectModel;
     
    namespace TestFormulaViaCodeDom
    {
        public class DynamicFormula
        {
            private string _formula;
            private List<string> _compilerMessages = new List<string>();
            private CompilerErrorCollection _compilerErrors;
     
            private string _language = "CSharp";
            private readonly string _nameSpace;
            private const string _className = "formula";
            private const string _methodName = "compute";
            private int _warningLevel = 1;
            private Type[] _classToParse = { typeof( Math ) };
            private string[] _nameSpacesToAdd =  { "System" };
            private string[] _referencesToAdd =  { "System.DLL" };
     
     
            public DynamicFormula( string formula )
            {
                _formula = formula.Trim();
                // ensure unique namespace to avoid potential conflict in app domain
                _nameSpace= "Dyngen_" + Guid.NewGuid().ToString().Replace('-','_'); // "-" char is invalid in names
            }
     
            /// <summary>
            /// True if compilation fails (must be checked if Compute returns NaN).
            /// </summary>
            public bool HasCompilationFailed
            {
                get
                {
                    return _compilerErrors.Count > 0;
                }
            }
     
            /// <summary>
            /// Return compile errors
            /// </summary>
            public CompilerErrorCollection CompilerErrors
            {
                get
                {
                    return _compilerErrors;
                }
            }
     
            /// <summary>
            /// Returns compiler messages (informations and errors)
            /// </summary>
            public ReadOnlyCollection<string> CompilerMessages
            {
                get
                {
                    return _compilerMessages.AsReadOnly();
                }
            }
     
            /// <summary>
            /// Compute the formula passed to the class .ctor
            /// </summary>
            /// <returns></returns>
            public double Compute()
            {
                double formulaResult = double.NaN;
                string parsedFormula = ParseForSymbolClasses( _formula );
                CodeCompileUnit unit = prepareCompileUnit( parsedFormula );
                Assembly dynamicAssembly = compileCode( unit, _language );
                if (dynamicAssembly != null)
                {
                    object formulaComputer = dynamicAssembly.CreateInstance(_nameSpace + "." + _className, true );
                    MethodInfo computeFormula = formulaComputer.GetType().GetMethod( _methodName );
                    formulaResult = (double)computeFormula.Invoke( formulaComputer, null );
                }
                return formulaResult;
            }
     
            private string ParseForSymbolClasses( string formula )
            {
                string parsedFormula = formula;
                foreach (Type symbolClass in _classToParse)
                {
                    parsedFormula = parseAndReplaceForSymbols( symbolClass, parsedFormula );
                }
                return parsedFormula;
            }
     
            /// <summary>
            /// Parse the formula to prefix  static methods (e.g. Cos, Sin, ... for Math class) and constants(PI, E) by class name
            /// </summary>
            /// <param name="formula"></param>
            /// <returns></returns>
            private string parseAndReplaceForSymbols(Type symbolClass, string formula)
            {
                int lastPos;
                int pos;
                MemberInfo[] mathSymbols = symbolClass.GetMembers( BindingFlags.Public | BindingFlags.Static );
                foreach (MemberInfo mathSymbol in mathSymbols)
                {
                    pos = 0;
                    lastPos = 0;
                    while (pos > -1  && lastPos < formula.Length - 1)
                    {
                        pos = formula.IndexOf( mathSymbol.Name, lastPos, StringComparison.InvariantCultureIgnoreCase );
                        // check if we have found a complete word (e.g not Tan in ATan for instance)
                        if (pos > -1)
                        {
                            char previousChar = '0';
                            bool usePrevChar = false;
                            char nextChar = '0';
                            bool useNextChar = false;
                            if (pos > 0)
                            {
                                previousChar = formula[pos - 1];
                                usePrevChar = true;
                            }
                            if (pos + mathSymbol.Name.Length  < formula.Length - 1)
                            {
                                nextChar = formula[pos + mathSymbol.Name.Length];
                                useNextChar = true;
                            }
                            if ((useNextChar && (char.IsLetterOrDigit( nextChar ) == false)) || (useNextChar == false))
                            {
                                if ((usePrevChar && (char.IsLetterOrDigit( previousChar ) == false)) || (usePrevChar == false))
                                {
                                    // it's a complete word --> replace SymbolName by ClassName.SymbolName and set the correct case sensitivity (remove and insert)
                                    formula = formula.Remove( pos, mathSymbol.Name.Length ).Insert( pos, symbolClass.Name + "." + mathSymbol.Name );
                                }
                            }
                            lastPos = pos + mathSymbol.Name.Length + symbolClass.Name.Length + 1;
                        }
                    }
                }
                return formula;
            }
     
            private Assembly compileCode( CodeCompileUnit compileunit, string language )
            {
                CompilerParameters compilerParameters = new CompilerParameters();
                foreach (string referenceToAdd in _referencesToAdd)
                {
                    compilerParameters.ReferencedAssemblies.Add( referenceToAdd );
                }
                compilerParameters.GenerateExecutable = false;
                compilerParameters.GenerateInMemory = true;
                compilerParameters.IncludeDebugInformation = false;
                compilerParameters.WarningLevel = _warningLevel;
     
                // Invoke compilation.
                CodeDomProvider provider = CodeDomProvider.CreateProvider( language );
                CompilerResults compilerResults = provider.CompileAssemblyFromDom( compilerParameters, compileunit );
                _compilerMessages.Clear();
     
                foreach (String compilerMessage in compilerResults.Output)
                {
                    if (String.IsNullOrEmpty( compilerMessage.Trim() ) == false)
                    {
                        _compilerMessages.Add( compilerMessage );
                    }
                }
     
                // Return the results of compilation.
                _compilerErrors = compilerResults.Errors;
                return (_compilerErrors.Count == 0) ? compilerResults.CompiledAssembly : null;
            }
     
            private CodeCompileUnit prepareCompileUnit( string formulaString )
            {
                CodeNamespace compute = new CodeNamespace( _nameSpace);
                foreach (string nsToAdd in _nameSpacesToAdd)
                {
                    compute.Imports.Add( new CodeNamespaceImport( nsToAdd ) );
                }
     
                CodeCompileUnit compileUnit = new CodeCompileUnit();
                compileUnit.Namespaces.Add( compute );
                CodeTypeDeclaration formulaComputing = new CodeTypeDeclaration(_className );
                compute.Types.Add( formulaComputing );
                CodeMemberMethod computeFormulaCode = new CodeMemberMethod();
                computeFormulaCode.Attributes = MemberAttributes.Public;
                computeFormulaCode.Name = _methodName;
                computeFormulaCode.ReturnType = new CodeTypeReference( typeof( double ) );
                CodeSnippetExpression formula = new CodeSnippetExpression( formulaString );
                CodeMethodReturnStatement computeFormulaReturnStatement = new CodeMethodReturnStatement( formula );
                computeFormulaCode.Statements.Add( computeFormulaReturnStatement );
     
                formulaComputing.Members.Add( computeFormulaCode );
                return compileUnit;
            }
        }
    }

  2. #2
    Rédacteur
    Avatar de Giovanny Temgoua
    Profil pro
    Étudiant
    Inscrit en
    Novembre 2003
    Messages
    3 830
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2003
    Messages : 3 830
    Points : 4 006
    Points
    4 006
    Par défaut


    Ecrire un tuto sur la compilation dynamique ne t'intéresserait pas ?


  3. #3
    Inactif  
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Janvier 2007
    Messages
    6 604
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Janvier 2007
    Messages : 6 604
    Points : 13 317
    Points
    13 317
    Par défaut
    Citation Envoyé par Giovanny Temgoua Voir le message


    Ecrire un tuto sur la compilation dynamique ne t'intéresserait pas ?
    Pourquoi pas ? ce n'est certainement pas la partie du framework .Net sur laquelle j'ai la maitrise la plus complète, mais comme il s'agit de quelque chose d'assez simple, je peux en effet m'en charger.

    Je pense que d'ici deux à trois semaines ce sera prêt.

  4. #4
    Rédacteur
    Avatar de Giovanny Temgoua
    Profil pro
    Étudiant
    Inscrit en
    Novembre 2003
    Messages
    3 830
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2003
    Messages : 3 830
    Points : 4 006
    Points
    4 006
    Par défaut
    Citation Envoyé par Bluedeep Voir le message
    Je pense que d'ici deux à trois semaines ce sera prêt.
    Excellent

    Vous pourrez toujours joindre le .doc en pièce jointe à votre message.

    Merci

  5. #5
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 753
    Points
    39 753
    Par défaut
    Citation Envoyé par Bluedeep Voir le message
    Pourquoi pas ? ce n'est certainement pas la partie du framework .Net sur laquelle j'ai la maitrise la plus complète, mais comme il s'agit de quelque chose d'assez simple, je peux en effet m'en charger.

    Je pense que d'ici deux à trois semaines ce sera prêt.
    Salut,

    Je viens de tomber sur cette discussion... tu as abandonné l'idée de l'article ?

    Sinon, y a une autre astuce sympa pour évaluer des formules : la méthode Eval de JScript

    Tu crées un fichier JScript avec la classe suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class JsMath
    {
        static function Eval(expression : String) : double
        {
            return eval(expression);
        };
    }
    Tu le compiles comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    > jsc /t:library JsMath.js
    Ensuite il ne reste plus qu'à ajouter une référence à la DLL générée, et utiliser la méthode JsMath.Eval...

    L'avantage de cette méthode par rapport à la compilation dynamique, c'est que ça ne pose pas de problèmes de de sécurité... seule une expression mathématique valide est exécutée, alors qu'avec la compilation dynamique on peut faire exécuter quasiment n'importe quoi

Discussions similaires

  1. [WD18] Erreur calcul dans compilation dynamique
    Par Fred 57220 dans le forum WinDev
    Réponses: 8
    Dernier message: 24/07/2013, 22h44
  2. Réponses: 19
    Dernier message: 20/10/2009, 10h16
  3. Réponses: 5
    Dernier message: 29/12/2005, 10h31
  4. Compression par Huffmann dynamique
    Par kael kael dans le forum Algorithmes et structures de données
    Réponses: 6
    Dernier message: 01/04/2004, 21h51
  5. Problème mémoire avec une dll par chargement dynamique
    Par widze19 dans le forum C++Builder
    Réponses: 6
    Dernier message: 15/12/2003, 13h20

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo