Execute a condition (if/Else) that is inside a string in c# 4.5

Asked

Viewed 626 times

3

I own a class that generates files csv from a query, and that file may have several templates, some with conditions to display value x or y.

These conditions are within a database and when I recover these data the expression comes within a string. I used Reflection to do this and it has worked, but it has put a lot on the performance of the application, given that there are many lines.

I wonder if there is some other way to execute an expression of if that is inside a string.

Example of if:

string Exp = "A == B ? 123 : 345"

Follow the function I’ve done:

string source = @"
    namespace cond
    {
        public class Bar
        {
            public string resultadoexpressao()
            {
                return "+expressao+";
            }
        }
    }";

Dictionary<string, string> providerOptions = new Dictionary<string, string>
{
        {"CompilerVersion", "v3.5"}
};

CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);

CompilerParameters compilerParams = new CompilerParameters
{
    GenerateInMemory = false,
    GenerateExecutable = false
};

CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source);

object o = results.CompiledAssembly.CreateInstance("cond.Bar");

MethodInfo mi = o.GetType().GetMethod("resultadoexpressao");

string resultado = (string)mi.Invoke(o, null);   

return resultado;

2 answers

0

If the expression is saved in the database, you can change it to the SQL itself to run the if/else, in case, if SQL Server, change the condition to a case, generate the query dynamically, loading the data of the parameterized tables and run it with sp_sqlexec and return the value already treated 123 or 345.

0


If you are on time, and have a desire to learn something cool, you can create a parser of expressions very simple for this.

A good recipe to follow in a parser is to create the read methods like this:

DadoDeRetorno LerAlgumaCoida(string codigo, ref int pos)
{
    // lê algo do código e avança a posição de leitura
}

That one DadoDeRetorno depends on what you want to do.

  • interpret: in that case the output of most methods is the result obtained from the interpretation. As you will not know beforehand the type to be returned, so it will be object even.

  • generate syntactic tree: then the output will be a syntactic tree node. This is the case for example, you want to use LINQ Expression Trees, which you can then compile, which will give you a great performance gain.

  • compile: few languages can be compiled directly from code, but an expression parser is probably an exception. In this case, you probably want to return a string/array already with the translation to the target language.

For every thing that is successfully handled, you should update the variable pos. In addition, the return of each function should indicate whether or not the method was successful to facilitate things.

You will need methods like this model to read spaces, to read variable names, to read numbers, among others:

bool LerEspacos(string codigo, ref int pos); // retorna false se não ler nada
string LerNomeDeVariavel(string codigo, ref int pos); // retorna null se não ler nada
int? LerNumeroInteiro(string codigo, ref int pos); // retorna null se não ler nada

Each can also receive other values relevant to each type of parser. For example, when making an interpreter, you need to pass the values of the variables. If you are a compiler, you may need to pass a list of the registrars in use and maybe even change this list.

You will also need a method for processing expressions. This goes into the same model, but within it, the implementation is a little bit more complicated.

Expression LerExpressao(string codigo, ref int pos, Contexto contexto);

For this case you need two stacks, one of operators, and the other of operands.

Whenever an operand is found, you stack it in the list of operands.

When an operator is found, you need to stack it into the stack of operators, but only if it has greater precedence than what is already in the stack. Otherwise, it is necessary to perform the operation that is in the stack with the operands that are in the operand stack.

Example of the steps of a two-stack interpreter:

Entrada: 1 + 3 * 5 + 8

// Lê 1 da entrada e põe na pilha de operandos
Operandos: 1
Operadores: 

Entrada: + 3 * 5 + 8

// Lê "+" da entrada e põe na pilha de operadores
Operandos: 1
Operadores: +

Entrada: 3 * 5 + 8

// Lê 3 da entrada e põe na pilha de operandos
Operandos: 1 3
Operadores: +

Entrada: * 5 + 8

// Lê "*" da entrada e põe na pilha de operadores
Operandos: 1 3
Operadores: + *

Entrada: 5 + 8

// Lê 5 da entrada e põe na pilha de operandos
Operandos: 1 3 5
Operadores: + *

Entrada: + 8

// Não pode empilhar + sobre *
// Executa a operação 3 * 5 = 15
// Remove o operador * da pilha
// Remove os operandos 3 e 5 da pilha
// Adiciona o 15 e o + nas respectivas pilhas
Operandos: 1 15
Operadores: + +

Entrada: 8

Operandos: 1 15 8
Operadores: + +

// Acabou, agora basta ir executando todos os itens das pilhas
// 15 + 8 = 23

Operandos: 1 23
Operadores: +

// 1 + 23 = 24

Operandos: 24
Operadores:

// o resultado é o que sobra na pilha de operandos = 24

Browser other questions tagged

You are not signed in. Login or sign up in order to post.