C# - Avaliando Expressões Matemáticas


Como avaliar expressões matemáticas ???

Usando o namespace CodeDom do .NET Framework podemos contornar esse problema.

O namespace System.CodeDom fornece classes para representar a estrutura lógica do código-fonte, independente da sintaxe da linguagem.

As classes existentes no namespace CodeDOM retratam elementos sintáticos e comandos, também conhecidos com Tokens, existentes em um código fonte escrito em qualquer linguagem .Net. Todo e qualquer token existente na sintaxe da Intermediate language(IL), gerada pelos compiladores das linguagens que usualmente trabalhamos(C#, VB, etc), possui uma classe que o representa no CodeDOM. Por exemplo, existem classes que representam declarações de namespaces, criação de tipos, definição de estruturas, if’s, for’s, atribuições de variáveis, chamadas de métodos e quaisquer comandos que podemos codificar quando utilizamos .NET.

A classe abstrata CodeDomProvider tem um papel essencial na arquitetura descrita acima, pois ela define como deve ser o comportamento padrão para as demais linguagens existentes na plataforma .Net. O diagrama ao lado demonstra as implementações concretas dos code providers fornecidos pela própria Microsoft no .Net Framework: J#(VJSharpCodeProvider), C#(CsharpCodeProvider)e VB(VBCodeProvider), respectivamente. (http://msdn.microsoft.com/pt-br/library/cc517997.aspx)

Alguns usos comuns do CodeDOM incluem:

No exemplo deste artigo temos uma aplicação feita na linguagem C# que mostra como avaliar expressões matemáticas simples.

Abra o Visual C# 2010 Express Edition e crie um novo projeto Windows Forms Application com o nome AvaliacaoMatematica;

Abaixo o formulário da aplicação avaliando uma expressão matemática, calculando e exibindo o resultado em controles TextBox:

A seguir o código do formulário:

sing System;
using System.Windows.Forms;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Reflection;
namespace AvaliacaoMatematica
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private double ProcessarCommando(string command)
        {
            //Cria um provedor de Código C#
            CSharpCodeProvider mCodeProvider = new CSharpCodeProvider();
            // Cria os parmaetros para a compilação origem
            CompilerParameters cp = new CompilerParameters();
            cp.GenerateExecutable = false;         //Não precisa criar um arquivo EXE
            cp.GenerateInMemory = true;            //Cria um na memória
            cp.OutputAssembly = "TempModule"; // Isso não é necessário, no entanto, se usado repetidamente, faz com que o CLR 
                                                                     //não precisa carregar uma novo assembly cada vez que a função é executada.
            // A string abaixo é basicamente a casca do programa C #, que não faz nada, mas contém um método Avaliar() para nossos propósitos. 
            //Atenção: Isso deixa a aplicação app aberto a ataques de injeção,
            //Estou fazendo apenas para demonstração .
            string TempModuleSource = "namespace ns{" +
                                      "using System;" +
                                      "class class1{" +
                                      "public static double Evaluate(){return " + command + ";}}} ";  //Calcula a expressão

            CompilerResults cr = mCodeProvider.CompileAssemblyFromSource(cp, TempModuleSource);
            if (cr.Errors.Count > 0)
            {
                //Se um erro de compilação é gerado, iremoslançar uma exceção
                //A sintaxe estava errado - novamente, isso é deixado ao implementador para verificar sintaxe antes
                //Chamando a função. O código de chamada pode prender isso em um laço try, e notificar o usuário
                //O comando não foi compreendido, por exemplo.
                throw new ArgumentException("A expressão não pode ser avaliada, utiliza uma expressão C# válida...");
            }
            else
            {
                MethodInfo Methinfo = cr.CompiledAssembly.GetType("ns.class1").GetMethod("Evaluate");
                return (double)Methinfo.Invoke(null, null);
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                txtResultado.Text = ProcessarCommando(txtOperacao.Text).ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Erro a avaliar a expressão..." + ex.Message.ToString() + " Obrigado...");
            }
            //Exemplos
            //ProcessCommand("Math.PI").ToString());                       //Exibe 3.14159265358979
            //ProcessCommand("Math.Abs(-22)").ToString());             //Exibe 22
            //ProcessCommand("3-4+6+7+22/3+66*(55)").ToString()); //Exibe 3649
        }
    }
}

A classe CSharpCodeProvider fornece acesso a instâncias do gerador de código C# e do compilador de código.

A classe CompilerResults representa o resultado da compilação que é retornado a partir do compilador.

Essa classe contém informações sobre os resultados de uma compilação de uma implementação de interface ICodeCompiler:

Pegue o projeto completo aqui: AvaliacaoMatematica.zip (Abra o projeto no Visual C# 2010 Express ou superior.)

Ecl 12:1 Lembra-te também do teu Criador nos dias da tua mocidade, antes que venham os maus dias, e cheguem os anos em que dirás: Não tenho prazer neles;

Ecl 12:2 antes que se escureçam o sol e a luz, e a lua, e as estrelas, e tornem a vir as nuvens depois da chuva;

Ecl 12:3 no dia em que tremerem os guardas da casa, e se curvarem os homens fortes, e cessarem os moedores, por já serem poucos, e se escurecerem os que olham pelas janelas,

Ecl 12:4 e as portas da rua se fecharem; quando for baixo o ruído da moedura, e nos levantarmos à voz das aves, e todas as filhas da música ficarem abatidas;

Ecl 12:5 como também quando temerem o que é alto, e houver espantos no caminho; e florescer a amendoeira, e o gafanhoto for um peso, e falhar o desejo; porque o homem se vai à sua casa eterna, e os pranteadores andarão rodeando pela praça;

Ecl 12:6 antes que se rompa a cadeia de prata, ou se quebre o copo de ouro, ou se despedace o cântaro junto à fonte, ou se desfaça a roda junto à cisterna,

Ecl 12:7 e o pó volte para a terra como o era, e o espírito volte a Deus que o deu.

Referências:


José Carlos Macoratti