Clique aqui para ler todos os artigos desta edição
Entendendo Valores e Referências no C#
por Claudenir Andrade
ByRef, ByVal, Valor e Referência, conceitos fáceis de se aplicar e quando bem aplicados facilitam o processo de desenvolvimento.
No VB4 até o VB6, ByRef e ByVal poderiam ser tratados sem mistérios, se você tivesse uma variável inteira. Se houvesse a necessidade de passá-la a uma API ou função por referência, haveria a necessidade de declarar o uso da mesma como ByRef. O mesmo acontece com os tipos “strings”, embora o simples fato de inicializar o tipo string e passá-la como ByVal, você já estaria escrevendo na área de memória o qual alocou a variável. No entanto, isso se consegue com um Dim MyBufer as string * 40.
O objetivo deste artigo é comentar como isso é tratado na plataforma .NET, mais especificamente no C#, quando chamamos APIS, como enviar parâmetros por valor, por referência, em nossos métodos e funções internas e como efetuar o tratamento correto das passagens destes valores.
Tipos dos Tipos
Vamos nivelar o conhecimento com a criação de Tipos de Variáveis, que basicamente classificamos em duas classes Primitive Types e Class Types. A criação acontece quando você utiliza o método new. Ao criar uma variável do tipo integer ou int no C#, você cria uma variável do tipo Primitive Type, porém, quando você cria com a mesma palavra reservada new um objeto ou classe, você cria uma Class Type. A principal diferença é que uma contém o VALOR e outra contém uma REFERÊNCIA a um objeto que contém o valor. Com isso começamos a entender a base do significado das variáveis por valor e por referência. Veja um exemplo de como alocar duas áreas de memória contendo o mesmo valor 60 referentes as variáveis iMyInteger e iMyCopyInteger.
int iMyInteger = 60;
int iMyCopyInteger = iMyInteger;
Já neste código o mesmo não acontece porque ambas variáveis fazem referência ao mesmo objeto que por sua vez foram igualadas. Como são do mesmo tipo não é alocada duas áreas de memória para conter o mesmo valor.
SALDO cMySaldo = new SALDO(+50);
SALDO cMySubTotal = cMySaldo;
Isso ocorre porque no exemplo anterior estamos trabalhando com Primitive Type onde cada nova criação destes tipo é armazenado uma nova área de memória para alocação da mesma. No segundo código estamos trabalhando com um Class Type onde se trabalha com a mesma área de memória apenas referenciando o nome da variável ou o novo objeto declarado. Observe que sem percebermos trabalhamos com valores por valor e por referência.
Analise o seguinte código e veja as duas situações contempladas, passagem de uma variável do Tipo Primitive Type e do tipo Class Type.
private void button6_Click(object sender, System.EventArgs e)
{
int iN1, iN2;
cCalculatorRef iNRef = new cCalculatorRef();
iN1=iN2=1;
Valores.cCalculator.MyNumbers(iN1,iN2);
MessageBox.Show(iN1.ToString() + " - " + iN2.ToString());
iNRef.iNumberRef=0;
Valores.cCalculator.MyNumbersRef(iNRef);
MessageBox.Show(iNRef.iNumberRef.ToString());
}
Na execução teremos os seguintes OutPuts:
No primeiro MessageBox “1-1”; no segundo MessageBox “2”. Isso indica que na primeira chamada os valores das variáveis permaneceram inalteráveis e o valor da segunda chamada com um Class Type, sofreu alteração, entra com o valor 0 (zero) e sai como valor 2. Para a execução do código acima, adicione o namespace com as seguintes classes:
namespace Valores
{
class cCalculator
{
public static int MyNumbers( int iNumber1, int iNumber2)
{ iNumber1+=++iNumber2;
return iNumber1; }
public static void MyNumbersRef(cCalculatorRef iNumber1)
{ iNumber1.iNumberRef+=2; }
}
class cCalculatorRef
{ public int iNumberRef; }
}
Observe que estamos no primeiro método de nossa classe passando uma variável “tipada”, um INT normal e corrente, porém na segunda interação estamos passando um objeto da classe como parâmetro da função e sem nenhuma indicação explicita o comportamento é diferenciado. Na primeira se comporta como passagem por valor e na segunda como passagem por referencia.
Passando Parâmetros por Valor e por Referencia no C# de forma Explícita
Existem diversas maneiras de conseguir o mesmo resultado anterior nas funções declarando de forma explícita quando será por valor ou por referência. A palavra reservada REF indica ao compilador que a função receberá valores por referência e que qualquer “trabalho” realizado com os parâmetros devem ser alterados. Portanto, adicione a palavra reservada REF na declaração do método MyNumbers e na chamada do método:
Na declaração do método:
public static int MyNumbers( ref int iNumber1, int iNumber2)
Na chamada do método:
Valores.cCalculator.MyNumbers(ref iN1,iN2);
Observe que a palavra reservada REF deve ser utilizada tanto na chamada como na declaração do método e seus parâmetros. Existe uma condição a ser seguida quando se utiliza a palavra reservada REF no programa, que é a inicialização prévia das variáveis. Caso comente a seguinte linha de código, ocorrerá um erro em tempo de compilação: (195): Use of unassigned local variable 'iN1'.
iN1=iN2=1;
O mesmo acontece com os tipos CHAR e Arrays. Altere o código da função para contemplar os seguintes parâmetros na chamada e veja o comportamento:
private void button6_Click(object sender, System.EventArgs e)
{
int iN1, iN2;
char cN3;
char[] aN4 = new char[10];
cCalculatorRef iNRef = new cCalculatorRef();
iN1=iN2=1;
cN3='A';
Valores.cCalculator.MyNumbers(ref iN1,iN2,ref cN3, ref aN4);
MessageBox.Show(iN1.ToString() + " - " + iN2.ToString());
iNRef.iNumberRef=0;
Valores.cCalculator.MyNumbersRef(iNRef);
MessageBox.Show(iNRef.iNumberRef.ToString());
}
Altere o método para contemplar os parâmetros com ref e para alterar o conteúdo da variável array e char. Note que o resultado será o mesmo que com a variável inteira, o conteúdo será alterado e permanecerá com a alteração. O contrário do que aconteceria caso não fosse utilizado a palavra reservada REF.
public static int MyNumbers(ref int iNumber1, int iNumber2, ref char cN3, ref char[] aN4)
{
iNumber1+=++iNumber2;
cN3='B';
aN4 = "Teste".ToCharArray();
return iNumber1;
}
Conclusão
Mostrei neste artigo um dos potenciais da plataforma.Net que, quando aplicada, facilita a manipulação de strings e proporciona um ganho de produtividade, velocidade e segurança no desenvolvimento. Estas dicas são básicas para manipular arrays. Sugiro que você dedique alguns minutos por dia para descobrir e surpreende-se com o que a platalforma.Net pode fazer por você.
Download disponível em www.neoficio.com.br/msdn