C# #1: Parâmetros por referência

Olá pessoal, tudo certo!?

Semana passada durante uma aula da graduação, chegamos em um debate sobre os tipos de passagem de parâmetros nas linguagens de programação. Através disto, achei interessante escrever sobre a passagem de parâmetros por referência em C#.

Atualmente existem várias formas de passagem de parâmetros, sendo elas: cópia valor, cópia resultado, cópia valor resultado, referência e nome. A linguagem C# oferece suporte a parâmetros por cópia valor e referência. Entenda que parâmetros por referência não são tipos por referência (reference-types). Os tipos por referência mais conhecidos são class e array. Vamos ver um exemplo de como funciona na prática parâmetros por referência e tipos por referência.


// Este método irá alterar o valor da variável original, atribuindo-a o valor null
// Pessoa é um tipo por referência sendo passado por referência
static void AlterarReferenciaPessoa(ref Pessoa p)
{
    p.Nome = "João";
    p = null;
}

// Este método apenas irá alterar o valor da propriedade nome da variável p
// Pessoa é um tipo por referência
static void AlterarPessoa(Pessoa p)
{
    p.Nome = "José";
    p = null;
}

// Este método irá alterar apenas o valor da variável local
// int é um tipo comum (cópia)
static void IncrementarValor(int v)
{
    v += 2;
}

// Este método irá alterar o valor da variável que chamou o método
// int é um tipo comum (cópia) sendo passado por referência
static void IncrementarValorRef(ref int v)
{
    v += 2;
}

static void ExibirMensagens()
{
    Pessoa pessoa = new Pessoa();
    pessoa.Nome = "Marcos";

    Pessoa pessoa2 = new Pessoa();
    pessoa2.Nome = "Marcos Jr.";

    AlterarPessoa(pessoa);
    AlterarReferenciaPessoa(ref pessoa2);

    if (pessoa != null)
        Console.WriteLine("O nome é: " + pessoa.Nome); // exibe
    else
        Console.WriteLine("A pessoa não existe!"); // não exibe

    if (pessoa2 != null)
        Console.WriteLine("O nome é: " + pessoa2.Nome); // não exibe
    else
        Console.WriteLine("A pessoa2 não existe!"); // exibe

    int v1 = 4,
        v2 = 3;

    IncrementarValor(v1); // v1 = 4
    IncrementarValorRef(ref v2); // v2 = 5

    Console.WriteLine("v1 é " + v1 + " e v2 é " + v2); // v1 é 4 e v2 é 5

}

Como pode ser notado no código acima, a keyword ref define um parâmetro por referência em C#. Porém a linguagem C# oferece outra keyword para parâmetros por referência, que é out. As duas keywords possuem o mesmo comportamento, existindo apenas uma pequena diferença no funcionamento de cada uma.

Para passar uma variável por parâmetros utilizando a keyword ref, obrigatoriamente a variável deve estar inicializada, e esta pode ou não sofrer alterações dentro do método.

Para passar uma variável por parâmetro utilizando a keyword out, não necessariamente a variável precisa estar inicializada, no entanto, o método obrigatoriamente atribui um valor à variável. Em caso de uso da variável dentro do método, o valor inicial deve ser atribuído antes do uso.

Exemplo de funcionamento das keywords ref e out.

// não precisa atribui um valor à variável res
static void SomarRef(int v1, int v2, ref int res)
{
    res = v1 + v2;
}

// Obrigatoriamente deve atribuir um valor à variável res
static void SomarOut(int v1, int v2, out int res)
{
    res = v1 + v2;
}

/* Este método funciona
static void SomarRef2(int v1, int v2, ref int res)
{
    //res = v1 + v2;
}
*/

/* Este método não funciona
static void SomarOut2(int v1, int v2, out int res)
{
    //res = v1 + v2;
}*/

static void ExibirSoma()
{
    int v1, v2, res1;
    int v3, v4, res2;

    v1 = 2;
    v2 = 5;
    v3 = 9;
    v4 = 7;

    //SomarRef(v1, v2, ref res1); não funciona, res1 não está inicializada
    //SomarOut(v1, v2, out res2); funciona, res2 não precisa estar inicializada

    res1 = 0;
    res2 = 0;

    SomarRef(v1, v2, ref res1);
    SomarOut(v3, v4, out res2);

    Console.WriteLine("res1: " + res1 + ", res2: " + res2);
}

O .NET Framework faz um uso interessante da keyword out em métodos para converter strings para numéricos.


static void ExibirStringParaInt()
{

    string vs1 = "10",
           vs2 = "ab1";
    int v1, v2;

    if (Int32.TryParse(vs1, out v1))
        Console.Write("vs1 convertido com sucesso para int: "); // exibe
    else
       Console.Write("Houve uma falha ao converter o valor de vs1. v1: "); // não exibe

    Console.WriteLine(v1); // 10

    if (Int32.TryParse(vs2, out v2))
       Console.Write("vs2 convertido com sucesso para int: "); // não exibe
    else
       Console.Write("Houve uma falha ao converter o valor de vs2. v2: "); // exibe

    Console.WriteLine(v2); // 0
}

O uso de parâmetros por referência são uma característica muito interessante da linguagem C#. No entanto boas práticas de programação recomendam a não utilização de parâmetros por referência, visto que você “perde o controle sobre o valor” da variável quando chama o método. Como pode ser visto acima, o próprio .NET Framework faz o uso de parâmetros do tipo out para retornar o valor de uma conversão de string para integer.

Os códigos deste post estão no GitHub.

Referências

MSDN – Ref

MSDN – Out parameter modifier

MSDN – Passing Parameters

MSDN – Avoid out parameters

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s