Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)

viernes, 21 de mayo de 2010

Retornando un valor nulo en lugar de un mapa de bits

Tengo un método, dentro de una clase cualquiera programada en C#, que procesa un par de mapas de bits y además genera un tercer mapa de bits que es el valor de retorno:

public Bitmap Foo (string r1, string r2)
{
            Bitmap bmap = new Bitmap(r1);
            Bitmap bmap2 = new Bitmap(r2);
            Bitmap bmapR = new Bitmap(bmap.Width, bmap.Height, bmap.PixelFormat);

// hacer cosas, muchas muchas cosas con bmap y bmap2, y la imagen resultante va a bmapR
            bmap.Dispose();
            bmap2.Dispose();
            return bmapR;
}

El problema es que un mapa de bits consume una cantidad considerable de memoria, además todas esas muchas cosas sólo quiero hacerlas si bmap y bmap2 tienen las mismas dimensiones, además que bmapR sólo se crearía para este caso. Se podría programar un método intermedio que llamaría al método Foo sólo si se cumple la condición, pero eso implicaría crear bmap y bmap2 en este método.

Esta manera de solucionarlo ya no me estaba convenciendo.

Entonces recordé que en .Net todo es un objeto, y como tal todo puede tener un valor nulo. ¿Y si retorno un valor nulo (null) en lugar de un bitmap si la condición de las dimensiones iguales no se cumple? Luego podría evaluar si se devolvío un valor nulo o no.

Mi método modificado resultó ser:

public Bitmap Foo (string r1, string r2)
{
            Bitmap bmap = new Bitmap(r1);
            Bitmap bmap2 = new Bitmap(r2);

            if (bmap.Width != bmap2.Width || bmap.Height != bmap2.Height)
            {
                bmap.Dispose();
                bmap2.Dispose();
                return null;
            }

            Bitmap bmapR = new Bitmap(bmap.Width, bmap.Height, bmap.PixelFormat);
  // hacer cosas, muchas muchas cosas con bmap y bmap2, y la imagen resultante va a bmapR
            bmap.Dispose();
            bmap2.Dispose();
            return bmapR;
}


Y la forma de llamarla es:

private void Metodo() 
{
            UnaClase M = new UnaClase();
            Bitmap bt = M.Foo(ruta1, ruta2);

            if (bt == null)
                MessageBox.Show("Las imágenes deben tener las mismas dimensiones");
            else
                pictureBox.Image = bt;

         //   bt.Dispose();  Esta lìnea es mejor omitirla pues da problemas si está en el evento Click de un botón
 }

¡Funciona! Y me evito crear tres mapas de bits cuando no los necesito.

:D

viernes, 14 de mayo de 2010

Fixed o no Fixed

Tengo el siguiente código:

using System.Drawing;
namespace SomeNamespace {
unsafe class SomeClass{

      private void One() {
            Point[] esq = new Point[3];
            Point* p = &esq[0];
            Two(p);
            }
     private void Two(Point *p) {
            // Write your ad here :)
            }
      }
}

Y pareciera que no hubiera error, pues he declarado la clase como "insegura" y estoy pasando un puntero a un dato tipo "Point" como parámetro a otra función. Y aún así me sale este error:

"Error  1 Sólo se puede adquirir la dirección de una expresión de tipo unfixed de un inicializador de instrucción fixed"

C# no deja usar punteros no "fijos" (es decir, cuyo valor, o dirección a la que apuntan, se puedan modificar). Esto es debido al colector de basura, que está siempre monitoreando la memoria, desasignando espacios de memoria, borrando objetos o moviéndolos a otras ubicaciones. Un puntero "fixed" evita que su valor apuntado sea movido a otra ubicación en la memoria, y así poder asegurarse que su valor no será reemplazado por otro sin que nos demos cuenta.

El código "fixed" sería:

using System.Drawing;
namespace SomeNamespace {
unsafe class SomeClass{
      private void One()  {
            Point[] esq = new Point[3];
         
            fixed( Point* p = &esq[0])
                   {
                        Two(p);
                   }
             }
     private void Two(Point *p)  {
            // Write your ad here :)
            }
     }
}



Según la documentación de Microsoft, es posible evitar usar "fixed" si se separa espacio en la pila para nuestros punteros. Esto se hacce con "stackalloc". La memoria se libera cuando se sale del método donde se separó el espacio en la pila:

using System.Drawing;
namespace SomeNamespace {
unsafe class SomeClass{
      private void One() {
            Point[] esq = stackalloc Point[3];
            Two(&esq[0]);    // también es válido: Two(esq);
            }
     private void Two(Point *p) {
            // Write your ad here :) 
            }
      }
}