sábado, marzo 05, 2011

Cuando usar ByVal y ByRef en VB.Net

Generalmente se suele usar ByVal en la programación de una función, por ejemplo

Private Sub (ByVal as ,...)

End Sub

pero tambien podemos escribir la función de la siguiente manera:

Private Sub (ByRef as ,...)

End Sub

¿Y cual es la diferencia?

Hay una gran diferencia al momento de usar ByVal y ByRef que se tiene que tener en cuenta a la hora de programar una función, y es que

El paso de argumento tipo ByVal crea una copia de la variable original, por lo que cualquier modificación hecha a dicha variable no afectará a la variable original.

El paso de argumento tipo ByRef hace una referencia de la variable original, por lo que cualquier modificación hecha a dicha variable si afectará a la variable original.

Para entender mejor en el siguiente ejemplo una variable inicializada en 10, cuyo valor se pasa a una función que lo incrementa en 1, debiendo obtener como resultado final 11.


Usando ByVal:


Dim suma As Integer = 10
fIncrementar(suma)



Private Sub fIncrementar(ByVal Value As Integer)
Value = Value + 1
End Sub


Si ejecutamos este ejemplo podremos comprobar que el item con valor 10 no aumenta a 11 porque en la función fIncrementar al usar ByVal creamos una copia de la variabel cuyo valor modificado no solo se aplicara a la variable orginal, el resutado final es que el número sigue siendo 10.



Usando ByRef:


Dim suma As Integer = 10
fIncrementar(suma)



Private Sub fIncrementar(ByRef Value As Integer)
Value = Value + 1
End Sub


Al usar ByRef prodremos comprobar que el valor de la variable suma que en un principio estuvo en 10 ahora aumento a 11, debido a que ByRef hace que la variable original se referencie en el argumento de la función fIncrementar, y cualquier cambio hecho al argumento Value se aplicará inmediatamente a la variable suma.


Otro caso en que es necesario conocer la diferencia entre Byval y ByRef es cuando queremos modificar una colección que esta siendo usado en un foreach, como se sabe no se puede agregar y tampoco eliminar items de una lista que esta siendo usado en un foreach, si se intenta hacer esto el compilador de visual estudio mostrará el siguiente error "Colección modificada; puede que no se ejecute la operación de enumeración.".

para poder realizar cambios a una colección mientras se usa en un foreach hay un truco usando ByRef .

En el siguiente ejemplo elimino un item de la colección cuando esta siendo usado en un foreach, para ello uso ByRef para hacer referencia a la lista que contiene los items, asi cualquier item que elimine en la función se actualizara en la lista original.




Dim lista As New List(Of Integer)

lista.Add(1)
lista.Add(2)
lista.Add(3)

For Each Item As Integer In lista.ToArray
If Item = 3 Then
fRemove(lista, Item)
End If
Next


Private Sub fRemove(ByRef lList As List(Of Integer), ByVal Item As Integer)
lList.Remove(Item)
End Sub


Con este truco podemos eliminar o agregar un item de la lista genérica mientras esta siendo usado en un foreach.

Reutilizar eventos en Csharp

Muchas veces los eventos de cada formulario en una aplicación MDI repiten el mismo código, por ejemplo si queremos que se abra una ventana de ayuda al presionar F1 debemos repetir el mismo código en el evento KeyDown de todos los formularios y programar la llamada a la ventana de ayuda.

Para simplificar estos temas y reutilizar código podemos optar en crear solo una función que llame a la ventana de ayuda y se ejecute cada vez que el usuario presione F1.

El siguiente ejemplo tiene un formulario MDI y dos formularios hijos, donde cada vez que se presione la tecla F1 en los formularios hijos muestre un mensaje, ademas, en la parte inferior del formulario MDI debe mostrar el nombre del formulario hijo actual.

En el formulario MDI programamos la función que abre a los formularios hijos y se asigna los eventos Activated y KeyDown para los resultados que deseamos conseguir.


//Funciones Generales

//Abrir los formularios hijos
private void fOpenWindows(Form value)
{
value.MdiParent = this;
value.Show();
//Para que los eventos de teclado se apliquen a todo el formulario
value.KeyPreview = true;
//Reutilizamos el evento activate en el formulario que vamos a abrir
value.Activated += new System.EventHandler(EvtActivate_Activated);
//Reutilizamos el evento KeyDown en el formulario que vamos a abrir
value.KeyDown += new KeyEventHandler(EvtKeyDown_KeyDown);
this.tslNomFormularioActivo.Text = value.Text;
}

//Mostrar el nombre del formulario activo
private void EvtActivate_Activated(Object sender, EventArgs e)
{
//Mostrar el nombre del formulario Activo en la parte
//inferior del MDI
this.tslNomFormularioActivo.Text = ((Form)sender).Text;
}


//Tecla presionada por el usuario
private void EvtKeyDown_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
//Mostrar un mensaje cuando se presione F1
if (e.KeyCode == Keys.F1)
{
MessageBox.Show("Usted presionó la tecla F1");
}
//Mostrar un mensaje cuando se presione F2
else if (e.KeyCode == Keys.F5)
{
MessageBox.Show("Usted presionó la tecla F5");
}
//Cuando se presione teclas combinadas que usen Ctrl + {tecla}
else if (e.Control == true)
{
//cuando el usuario presiona Ctrl + C mostrar un mensaje
if (e.KeyCode == Keys.C)
{
MessageBox.Show("Usted presionó la combinación de teclas Ctrl + C");
}
//cuando el usuario presiona Ctrl + V mostrar un mensaje
else if (e.KeyCode == Keys.V)
{
MessageBox.Show("Usted presionó la combinación de teclas Ctrl + V");
}

}
}


Código para abrir los dos formularios hijos.


private void form1ToolStripMenuItem_Click(object sender, EventArgs e)
{
Form1 wOpen = new Form1();
fOpenWindows(wOpen);
}

private void form2ToolStripMenuItem_Click(object sender, EventArgs e)
{
Form2 wOpen = new Form2();
fOpenWindows(wOpen);
}


La lógica nos sirve tanto en aplicaciones de escritorio y aplicaciones web. En Asp.Net tambien podemos reutilizar eventos.

Como vemos no se ha programado nada en los dos formularios hijos, logrando de esta manera reducir el código de nuestra aplicación.