Una respuesta que di cuando alguien me preguntó sobre la reutilización de componentes en desarrollo de software:
Las librerías de la plataforma .Net agrupan clases que sirven para hacer todo: mandar y recibir datos de la consola, controlar el puerto serial, los trabajos de impresión, contienen controles tales como formularios para crear ventanas, botones, cajas de texto, etc.
.Net se basa en la reutilización de pedazos de software (las librerías) y la POO. En .Net todo son clases (las librerías, también llamadas Namespaces agrupan a las clases) y objetos que referencian a estas clases y pueden "heredar" o hacer uso de sus métodos, dentro de éstos están los algoritmos.
En el generador de laberintos se usan mucho las clases y objetos que proporciona el namespace System.Drawings. Yo no he creado ningún componente, todo el laberinto se crea usando este namespace. El objeto de tipo Graphics hereda los métodos necesarios para dibujar el laberinto.
Una explicación más detallada de cómo lo programé está en mi web:
http://yelinna.comoj.com/eam/maze_generator.html
http://yelinna.comoj.com/eam/maze_generator_hex.html
El equivalente de .Net para Java es la JVM o la máquina virtual. Para el Visual C++ existe además el MFC (Microsoft Foundation Clases) que hacen un poco más fácil trabajar directamente con las funciones mismas del sistema operativo (las Apis), que son funciones que se pueden usar para hacer las mismas cosas que con el .Net o la JVM, pero dependen mucho del tipo y versión del sistema operativo (los virus para Windows son básicamente muchas llamadas a las Apis, con ellas se puede ver la libreta de direcciones de Outlook, controlar el envío por red, copair en carpetas específicas del disco duro, ver el usuario actual del sistema, etc).
Como verás, las apis son otro componente reutilizable.
Códigos de ejemplo para aprender distintas tecnologías, o lo que sucede cuando a una cuarentona se la deja sola con una computadora
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, 2 de julio de 2010
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:
// hacer cosas, muchas muchas cosas con bmap y bmap2, y la imagen resultante va a 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:
if (bmap.Width != bmap2.Width || bmap.Height != bmap2.Height)
{
bmap.Dispose();
bmap2.Dispose();
return null;
}
Y la forma de llamarla es:
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
public Bitmap Foo (string r1, string r2)
{
Bitmap bmap = new Bitmap(r1);
Bitmap bmap2 = new Bitmap(r2);
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();
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);
Bitmap bmap2 = new Bitmap(r2);
{
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();
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:
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:
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 = new Point[3];
Point* p = &esq[0];
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])
{
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 :)
}
}
}
lunes, 29 de marzo de 2010
Un dato muy útil para VB6
Por qué no se debe poner SetFocus en el evento Load de un formulario:
Don't use SetFocus on Invisible Controls
Esto se debe a que el Visual basic llama al método SetFocus antes de que los controles del formulario se hayan cargado.
Don't use SetFocus on Invisible Controls
Esto se debe a que el Visual basic llama al método SetFocus antes de que los controles del formulario se hayan cargado.
viernes, 20 de noviembre de 2009
Cómo saber si un PictureBox tiene una imagen cargada en VB6 y C#
En VB6:
If PictureBox1.Picture = 0 Then
'PictureBox sin imagen
Else
'PictureBox con imagen
End If
en C#:
if (pictureBox1.Image == null)
{
//picturebox sin imagen
}
else
{
//picturebox con imagen
}
If PictureBox1.Picture = 0 Then
'PictureBox sin imagen
Else
'PictureBox con imagen
End If
en C#:
if (pictureBox1.Image == null)
{
//picturebox sin imagen
}
else
{
//picturebox con imagen
}
lunes, 12 de octubre de 2009
ListBox y ListView para Visual basic 6, algunos detalles.
Índices de los elementos de un ListBox: Va desde 0 hasta ListBox.ListCount-1.
Índices de los elementos de un ListView: Va desde 1 hasta ListView.ListItems.count
Para capturar el texto del elemento seleccionado en un ListBox se llama a su propiedad "Text": ListBox.Text
El equivalente para un ListView:
Lo que va entre paréntesis es el índice del elemento del que queremos saber el texto. En este caso es el ídnice del elemento seleccionado.
Para copiar en un ListView los elementos de un ListBox (y además se muestra cómo recorrerlos):
El ListView trabaja con dos imageList si se desea poner íconos grandes y pequeños a sus elementos. Para el ejemplo no se asigna ningún ícono, por eso hay parámetros que no se usan en la propiedad ListItems.Add (donde están las comas que van seguidas).
Para borrar todos los elementos de un ListBox y un ListView se usa la propiedad Clear:
Cómo Usar el Control ListView en la página del Guille
Índices de los elementos de un ListView: Va desde 1 hasta ListView.ListItems.count
Para capturar el texto del elemento seleccionado en un ListBox se llama a su propiedad "Text": ListBox.Text
El equivalente para un ListView:
ListView.ListItems(ListView.SelectedItem.Index).Text
Lo que va entre paréntesis es el índice del elemento del que queremos saber el texto. En este caso es el ídnice del elemento seleccionado.
Para copiar en un ListView los elementos de un ListBox (y además se muestra cómo recorrerlos):
For i = 0 To ListBox.ListCount - 1
ListView.ListItems.Add , , ListBox.List(i)
Next
El ListView trabaja con dos imageList si se desea poner íconos grandes y pequeños a sus elementos. Para el ejemplo no se asigna ningún ícono, por eso hay parámetros que no se usan en la propiedad ListItems.Add (donde están las comas que van seguidas).
Para borrar todos los elementos de un ListBox y un ListView se usa la propiedad Clear:
ListBox.Clear
ListView.Clear
Cómo Usar el Control ListView en la página del Guille
miércoles, 9 de septiembre de 2009
Conversión de rutas en formato URI a formato del sistema operativo.
Ya he hablado de este problema en http://toyscaos.tripod.com/hist_csharp.html
La clase "Process" no tiene problemas con el formato URI, pero hay otros casos en que es necesario convertir este tipo de rutas a archivos en sus equivalentes "normales" (como al cargar una imagen en un picture box).
En VB se puede usar:
path = "..\some file"
Donde "path" es un dato de tipo string que almacena la ruta relativa a nuestra aplicación, algo necesario cuando queremos cargar algo ubicado en su misma carpeta, cualquiera que esta sea. Pero esta forma de declarar rutas relativas tiende a generar errores. Una manera más segura, es:
C#:
path = System.IO.Path.GetDirectoryName
(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
path = string.Concat(path, "\\some file");
VB:
path = System.IO.Path.GetDirectoryName_
(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)
path = String.Concat(path, "\some file")
Pero esto nos da las rutas en formato URI. La forma de convertir a formato "normal" (eliminar la cadena "file://" que tiene al comienzo) es:
C#:
string path;
path = System.IO.Path.GetDirectoryName
(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
path = string.Concat(path, "\\some file");
char[] charBuf = path.ToCharArray();
char[] charbuf2 = new char[path.Length - 6];
for (int i = 6; i < path.Length; i++)
charbuf2[i - 6] = charBuf[i];
string ruta = "";
ruta = new string(charbuf2);
VB:
Dim path As String
Dim i As Integer
Dim charbuf As String
path = System.IO.Path.GetDirectoryName_
(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)
path = String.Concat(path, "\some file")
charbuf = ""
For i = 7 To path.Length
charbuf = String.Concat(charbuf, path.Chars(i - 1))
Next
ruta = charbuf
Happy Coding!
La clase "Process" no tiene problemas con el formato URI, pero hay otros casos en que es necesario convertir este tipo de rutas a archivos en sus equivalentes "normales" (como al cargar una imagen en un picture box).
En VB se puede usar:
path = "..\some file"
Donde "path" es un dato de tipo string que almacena la ruta relativa a nuestra aplicación, algo necesario cuando queremos cargar algo ubicado en su misma carpeta, cualquiera que esta sea. Pero esta forma de declarar rutas relativas tiende a generar errores. Una manera más segura, es:
C#:
path = System.IO.Path.GetDirectoryName
(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
path = string.Concat(path, "\\some file");
VB:
path = System.IO.Path.GetDirectoryName_
(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)
path = String.Concat(path, "\some file")
Pero esto nos da las rutas en formato URI. La forma de convertir a formato "normal" (eliminar la cadena "file://" que tiene al comienzo) es:
C#:
string path;
path = System.IO.Path.GetDirectoryName
(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
path = string.Concat(path, "\\some file");
char[] charBuf = path.ToCharArray();
char[] charbuf2 = new char[path.Length - 6];
for (int i = 6; i < path.Length; i++)
charbuf2[i - 6] = charBuf[i];
string ruta = "";
ruta = new string(charbuf2);
VB:
Dim path As String
Dim i As Integer
Dim charbuf As String
path = System.IO.Path.GetDirectoryName_
(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)
path = String.Concat(path, "\some file")
charbuf = ""
For i = 7 To path.Length
charbuf = String.Concat(charbuf, path.Chars(i - 1))
Next
ruta = charbuf
Happy Coding!
domingo, 23 de agosto de 2009
Limpiar un Combobox (para Visual Studio 2008)
Si se tiene un control tipo combobox y se lo desea limpiar (borrar todos los elementos de su lista) en VB6 bastaba hacer:
Combobox.Clear
En .Net se debe añadir la propiedad "Items":
Combobox.Items.Clear()
Si el combobox tiene la propiedad "DataSource" enlazada a una base de datos, antes se deberá "romper" el enlace a ésta. Una manera es asignarle un valor nulo:
En VB:
Combobox.DataSource = Nothing
En C#:
Combobox.DataSource = null;
Si no se rompe el enlace, el compilador no nos permitirá limpiarlo. "Nothing" es el valor nulo en VB, "null" es el valor nulo para C#, C++, C o Java. Ambas son palabras reservadas en sus respectivos lenguajes.
Combobox.Clear
En .Net se debe añadir la propiedad "Items":
Combobox.Items.Clear()
Si el combobox tiene la propiedad "DataSource" enlazada a una base de datos, antes se deberá "romper" el enlace a ésta. Una manera es asignarle un valor nulo:
En VB:
Combobox.DataSource = Nothing
En C#:
Combobox.DataSource = null;
Si no se rompe el enlace, el compilador no nos permitirá limpiarlo. "Nothing" es el valor nulo en VB, "null" es el valor nulo para C#, C++, C o Java. Ambas son palabras reservadas en sus respectivos lenguajes.
lunes, 10 de agosto de 2009
Diferencias entre las versiones del Matlab
Matlab 6.5: Puede instalarse en Windows 98 SE y en Windows 2000 y en computadoras con 128Mb de RAM y procesador de 500MHz.
Matlab 7: Puede instalarse en Windows 2000 y en Windows XP y en computadoras con 512Mb de RAM y procesador de un núcleo de 2GHz. No corre en computadoras con procesadores de doble núcleo. Tampoco ejecuta los programas hechos con el Guide de Matlab 6.5 ni con Simulink del Matlab 6.5
Matlab 7.6: Puede instalarse en Windows XP y Windows Vista. Corre en computadoras con procesador de doble núcleo. Puede ejecutar los programas hechos con el Guide del Matlab 7. No es recomendable correrlo en computadoras con menos de 1Gb de RAM.
Página oficial de Matlab: http://www.mathworks.com/
Alternativas gratuitas:
Octave
FreeMat
Scilab
Matlab 7: Puede instalarse en Windows 2000 y en Windows XP y en computadoras con 512Mb de RAM y procesador de un núcleo de 2GHz. No corre en computadoras con procesadores de doble núcleo. Tampoco ejecuta los programas hechos con el Guide de Matlab 6.5 ni con Simulink del Matlab 6.5
Matlab 7.6: Puede instalarse en Windows XP y Windows Vista. Corre en computadoras con procesador de doble núcleo. Puede ejecutar los programas hechos con el Guide del Matlab 7. No es recomendable correrlo en computadoras con menos de 1Gb de RAM.
Página oficial de Matlab: http://www.mathworks.com/
Alternativas gratuitas:
Octave
FreeMat
Scilab
viernes, 7 de agosto de 2009
Microsoft Connect y cómo instanciar una clase en C#
Sucedió que necesitaba trabajar con manipulación de imágenes en C# (en el namespace System.Drawing) pero en una aplicación de consola. Los problemas que tuve al declarar objetos globales del tipo ColorPalette e Image me llevaron a escribirles a la gente de Microsoft Connect (siempre programo mi C# en el Visual Studio Express). Lo que me respondieron está en este link (en inglés).
En la solución que dan dicen que hace falta crear una instancia a la clase Process. Crear una instancia es simplemente usar: AnyClass p = new AnyClass(); pues .net asigna un constructor por defecto a todas las clases, aún si este no se declara (también hay un destructor implícito).
La solución la programé yo misma, luego de leer las instrucciones de Alex Turner.
Haciendo simplemente:
Image fotoJpg;
ColorPalette cp;
static void Main(string[] args)
{
Program p = new Program();
p.fotoJpg = Image.FromFile("C:\\background-black.jpg");
p.cp = p.fotoJpg.Palette;
}
}
Ya tenía los objetos ColorPalette e Image como globales.
See ya next time!
En la solución que dan dicen que hace falta crear una instancia a la clase Process. Crear una instancia es simplemente usar: AnyClass p = new AnyClass(); pues .net asigna un constructor por defecto a todas las clases, aún si este no se declara (también hay un destructor implícito).
La solución la programé yo misma, luego de leer las instrucciones de Alex Turner.
Haciendo simplemente:
Image fotoJpg;
ColorPalette cp;
static void Main(string[] args)
{
Program p = new Program();
p.fotoJpg = Image.FromFile("C:\\background-black.jpg");
p.cp = p.fotoJpg.Palette;
}
}
Ya tenía los objetos ColorPalette e Image como globales.
See ya next time!
Suscribirse a:
Entradas (Atom)