Switch Alternative Inside Switch

Asked

Viewed 1,839 times

6

I’m making a decision structure that depends on more than one variable, but readability and maintenance using if or switch are terrible.

What alternatives do I have for this case?

Example of switch nested:

switch (var1)
{
    case "A":
        switch (var2)
        {
            case 0:
                ...
                break;
            case 1:
                ...
                break;
            case 2:
                ...
                break;
        }
        break;
    case "B":
        switch (var2)
        {
            case 0:
                ...
                break;
            case 1:
                ...
                break;
            case 2:
                ...
                break;
        }
        break;
}

In the real situation I’m doing a style program Crawler that from a single form you need to replicate the information by filling out similar forms on various other websites.
In my form I have, for example, a combo with dozens of professions, hence need that the same profession that is chosen here is mapped to the corresponding option of the other site.

The option that in mine combo is Engenheiro (a) on a website is Engenheiro, in another is Engenheiro (outros), in another is Engenheiro(a)/Arquiteto(a), in another is Engenheiro/Arquiteto...

So in the actual case, the variables that I use in Switch..Case are the name of the site I am mapping and the option selected on combo.

  • 2

    What kind of alternative do you want? What’s your problem? You’re just finding it ugly?

  • 3

    if I have to, I extracted the code for functions

  • I fixed the Need ( do ) a :)

  • The problem is basically being ugly. I just put an example because the real code is VERY MUCH bigger. Do it is not so laborious because I was riding the switch..case with regex, but reading it and maintenance is very bad. Registering everything in a bank would be an alternative, but the values will hardly change and access to the bank would slow down. Maybe you just don’t have a choice And I’ll have to live with that :(

  • I think the best way is to extract each secondary switch for a more generic function, is that it depends a lot on the case, with luck and some abstraction you can even use this function again, but it is very vague your question so there is no way to help you more than that. I’m sorry.

  • The switch() internal to the case "A": shall be the same as in case "B":?

  • Yes, @Randrade os switch internal may be the same. I put more explanation at the end of the question speaking my real use, I hope it helps.

  • In this case, you can create a method that receives the value of var2 and returns the expected value. You will pass this method in each switch, but for maintenance will be much better.

Show 3 more comments

2 answers

11


In fact the command switch-case is enough ugly and almost always expendable.

Instead of using this command or long chains of if-elseif-elseif..., you can use dictionaries.

In your case, you would fill a dictionary of dictionaries, with the following semantics:

profissão -> site -> profissão naquele site

So instead of making a long, confusing, ugly, hard-to-maintain imperative code like this:

switch (profissao_selecionada)
{
    case "Engenheiro(a)":
        switch (site_selecionado)
        {
            case "site A":
                profissao_no_outro_site = "Engenheiro/Arquiteto";
                break;
            case "site B":
                profissao_no_outro_site = "Engenheiro (outros)";
                break;
        }
        break;
    case "Astronauta":
        switch (site_selecionado)
        {
            case "site A":
                profissao_no_outro_site = "Cosmonauta";
                break;
            case "site B":
                profissao_no_outro_site = "Espaçonauta";
                break;
        }
        break;
}

you can associate profession and site in a more declarative way:

profissoes["Engenheiro(a)"]["site A"] = "Engenheiro/Arquiteto";
profissoes["Engenheiro(a)"]["site B"] = "Engenheiro (outros)";

profissoes["Astronauta"]["site A"] = "Cosmonauta";
profissoes["Astronauta"]["site B"] = "Espaçonauta";

And then, in just one line, without any switch-case, you check how the profession selected in the combo is defined on a particular site, more or less like this:

profissao_no_outro_site = profissoes[profissao_selecionada][site_selecionado];

Note that you can use the dictionary itself to popular the professions in the combo instead of replicating them in the combo and code, so as to maintain items in one place.

Full example

If you have any questions, see this full functional example:

public partial class Form1 : Form
{
    Dictionary<String, Dictionary<String, String>> profissoes;

    public Form1()
    {
        InitializeComponent();

        var engenheiro = new Dictionary<String, String>();
        engenheiro["site A"] = "Engenheiro";
        engenheiro["site B"] = "Engenheiro (outros)";
        engenheiro["site C"] = "Engenheiro(a)/Arquiteto(a)";

        var astronauta = new Dictionary<String, String>();
        astronauta["site A"] = "Cosmonauta";
        astronauta["site B"] = "Espaçonauta";
        astronauta["site C"] = "Lunático";

        profissoes = new Dictionary<String, Dictionary<String, String>>();
        profissoes["Engenheiro(a)"] = engenheiro;
        profissoes["Astronauto(a)"] = astronauta;
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        foreach (var profissao in profissoes)
            comboProfissoes.Items.Add(profissao.Key);
    }
    private void seleciona_Click(object sender, EventArgs e)
    {
        var profissaoSelecionada = (String)comboProfissoes.SelectedItem;
        var mensagem = "A profissão " + profissaoSelecionada 
            + " no site B é " + profissoes[profissaoSelecionada]["site B"];
        MessageBox.Show(mensagem);
    }
}

Whose "output" looks like this:

inserir a descrição da imagem aqui

Storing complex code in the dictionary

And if in every block marry you needed to run some lines of code instead of simply setting a variable like I did in the above example? The dictionary is still the solution!

In the example above, we use the dictionary to store the profession legend on another site, but we can use it to store any object and not just strings. That is, we can add blocks of code in the dictionary to be executed at the right time, dispensing the switch-case in virtually any situation.

Since the dictionary supports objects and almost everything in C# is an object, there are several ways to store code blocks in the dictionary: you can store actions, functions, can define your own delegates, interfaces, abstract classes... The sky is the limit :D

Note that the dictionary key itself can also be any object, further exposing the horizons.

A small example

Let’s assume that same block switch from above do more than just set a variable:

switch (profissao_selecionada)
{
    case "Engenheiro(a)":
        switch (site_selecionado)
        {
            case "site A":
                // faz um trabalho muito específico aqui
                break;
            case "site B":
                // faz outro trabalho específico aqui
                break;
        }
        break;
    case "Astronauta":
        switch (site_selecionado)
        {
            case "site A":
                // mais trabalhos complexos
                break;
            case "site B":
                // outros serviços impensáveis
                break;
        }
        break;
}

What you have to do is to encapsulate each code block in methods with a common signature:

void Metodo_01()
{
    // faz um trabalho muito específico aqui
}
void Metodo_02()
{
    // faz outro trabalho específico aqui
}
void Metodo_03()
{
    // mais trabalhos complexos
}
void Metodo_04()
{
    // outros serviços impensáveis
}

Hence you declare a delegate with this common signature (or uses the native . Net delegates, if you prefer):

delegate void ProcessadorProfissoes();

And then fill in the dictionary as before, only now adding methods to it instead of adding strings:

var processadorEngenheiro = new Dictionary<String, ProcessadorProfissoes>();
processadorEngenheiro["site A"] = Metodo_01;
processadorEngenheiro["site B"] = Metodo_02;

var processadorAstronauta = new Dictionary<String, ProcessadorProfissoes>();
processadorAstronauta["site A"] = Metodo_03;
processadorAstronauta["site B"] = Metodo_04;

processadorProfissoes = new Dictionary<string,Dictionary<string, ProcessadorProfissoes>>();
processadorProfissoes["Engenheiro(a)"] = processadorEngenheiro;
processadorProfissoes["Astronauta"] = processadorAstronauta;

Now, to run the code block referring to the selected profession and site, just this simple line, without switch-case, without if...:

processadorProfissoes[profissaoSelecionada][siteSelecionado]();

Completion

Block code switch-case and long blocks if-If if most often can be better expressed through dictionaries or combination of dictionaries, simplifying code maintenance.

In the examples of this answer, strings and simple-signature methods have been stored in the dictionary; but we can store any type of object, so we can have methods with complex signatures, returning values, or full state-filled objects to perform complex jobs.

1

Could leave with default values

$valores = array(
    'carro' => array('item1' => 'pneu', 'item2' => 'pneu','item3' => 'pneu'),

    'moto' => array('item1' =>'gidao', 'item2' => 'pneu', 'item3' =>'quadro'),

    'caminhao' => array('item1' => 'volante', 'item2' =>'banco', 'item3' =>'porta'),

);

echo $values[$type][$item];

NOTE: I had not noticed that the question was for C#. As I already answered, understand only the idea.

  • Which is a great idea; the same one that was already given in the other answer, including.

Browser other questions tagged

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