Nov 03 2008

ObsoleteAttribute - Para refactorear y mantener compatibilidad {

Tag: C#, Mini Tips, VB.Net

Algo muy común a la hora de refactorear es que se elimine algún método o alguna clase, pero si estamos trabajando en un grupo de soluciones con referencias entre ellas, eliminar un método puede causar bastantes problemas… y molestias en quienes estén trabajando con nuestro código.

.Net ofrece una ayuda, es un atributo que se puede aplicar a clases, métodos, propiedades, variables y constantes y que sirve de alternativa para no romper compatibilidad, el attributo Obsolete() nos permite marcar como obsoleta una porción del código sin eliminarlo y nos brinda la posibilidad de mostrar un mensaje y generar una advertencia o bien un error a la hora de compilar.

Veamos un pequeño ejemplo en VB.Net y luego en C#

Public Class MiClase
 
	<Obsolete("Este constructor fue reemplazado por New(String, Boolean)", True)> _
	Public Sub New(Arg1 as String, Arg2 as String)
		' Código del ctor obsoleto
		' Ahora genera un error al compilar
	End Sub
 
	Public Sub New(Arg1 as String, Arg2 as Boolean)
		'Código del nuevo ctor
	End Sub
 
	<Obsolete("Esta propiedad está obsolete. Por favor eliminar toda referencia a la misma antes del release 2.0")> _
	Public ReadOnly Property IsObsolete() As Boolean
		Get
			Return True
		End Get
	End Property
 
End Class

class Ejemplo
{
    [Obsolete()]
    public const string MI_CONSTANTE = "NuevosProgramadores.com";
 
    [Obsolete("No usar esta variable. Utilizar la variable FechaHoraActual en su lugar.", false)]
    DateTime Fecha = DateTime.Now;
 
}

A simple vista se puede ver que la única diferencia entre su uso en VB.net y C# es que los atributos se aplican de manera diferente.

Por último veamos las 3 firmas de este atributo:

  • Obsolete() - Genera una advertencia (warning) sin descripción alguna.
  • Obsolete(String) - Genera una advertencia con con la descripción indicada en el parámetro.
  • Obsolete(String, Boolean) - Utiliza el parámetro String como descripción y el Boolean indica si genera una advertencia (false) o un error (true).

}


Oct 30 2008

Patrón Observador - Ejemplo en C# {

Tag: C#, Observador

El patrón observador define una relación de uno a muchos, en la que un objeto único, al que llamaremos observable deberá notificar cada vez que cambie de estado a muchos objetos, que llamaremos observadores.

El funcionamiento de este patrón de diseño puede compararse con el de una suscripción a una revista, ya que los observadores se suscriben y cada vez que la revista, (observable), se actualiza, la misma se envía a los observadores suscriptos.

La definición del patrón observador es la siguiente: define una dependencia entre objetos de uno a muchos, de manera tal que cuando un objeto cambia su estado, todos sus dependientes son notificados y actualizados automaticamente.

Para demostrar el uso de este patrón, se ha creado un ejemplo en el que tenemos un form principal que tiene 2 botones, uno para abrir múltiples instancias de observadores, que en nuestro caso serán forms que mostrarán un listado desde una fuente de datos, un archivo XML más concretamente; y otro botón para abrir nuestro simulador de edición, que no es más que un form que nos permitirá editar la fuente de datos y nos permitirá observar que cada vez que alteramos los datos nuestros observadores serán notificados y actualizados automáticamente.

Es importante destacar que para una correcta implementación del patrón se utilizan interfaces que definen el rol de una clase como Observable u Observador, con lo cual nos olvidamos, por ejemplo de cómo se comporta cada clase cuando es notificada de un cambio de estado.

Veamos el diagrama de clases y a continuación el enlace para descargar nuestra mini-aplicación que implementa el patrón observador en C#.

Ejemplo de Patrón Observador en C# (155.92 KB)

}


Sep 16 2008

Corte de Control - Algoritmo y Ejemplo en C# {

Tag: Algoritmos, C#

Un Corte de Control es un algoritmo muy usado a la hora de listar datos. Actualmente es más factible obtener los datos con una consulta SQL, pero el Corte de Control aún es una técnica válida.

Un Corte de Control se compone de 3 partes básicas: principio, cuerpo y fin, en pseudo código sería así:

while(i < cantidadElementos)
{
     principio; // inicializar variables y condiciones, obtener datos, etc.
 
     cuerpo; // recorrer los datos respetando la condición.
 
     fin; // mostrar los datos.
}

En nuestro ejemplo tendremos datos de alumnos y cursos, la estructura es como si fuese una base de datos, entonces nuestras tablas tendrían la siguiente estructura:

Alumnos Cursos
ID int ID int
IDCurso int Descripción string
Nombre string

Y nosotros necesitamos hacer un Corte de Control por ID de Cursos (criterio del corte) para listar los alumnos agrupados por curso, entonces el algoritmo se vería de la siguiente manera:

static void corteDeControl()
        {
            // -- principio del corte
            int i= 0; // variable utilizada para iterar en la lista de cursos
            int IDCursoAnterior = 0; // variable con la condición del corte
            Dictionary<int, List<alumno>> resultado = new Dictionary<int, List<alumno>>(); // resultado del corte
            Console.WriteLine("\n\nListado de alumnos por curso\n"); // imprimir encabezado
 
            // -- cuerpo del corte
            while (i < DS.Cursos.Count)
            {
                IDCursoAnterior = DS.Cursos[i].ID; // condición del corte
 
                while (i < DS.Cursos.Count && DS.Cursos[i].ID == IDCursoAnterior) // mientras se cumpla la condición del corte
                {
                    foreach (alumno a in DS.Alumnos) // buscar los alumnos que pertenezcan a ese curso
                    {
                        if (a.IDCurso == IDCursoAnterior)
                        {
                            if (resultado.ContainsKey(IDCursoAnterior))
                            {
                                resultado[IDCursoAnterior].Add(a);
                            }
                            else
                            {
                                List<alumno> lista = new List<alumno>();
                                lista.Add(a);
                                resultado.Add(IDCursoAnterior, lista);
                            }
                        }
                    }
                i++; // siguiente iteración
                }
            }
 
            //fin del corte
            imprimir(resultado); // mostrar el listado
        }

Resumiendo, en pseudo código, el cuerpo del corte de control se vería más o menos así:

while(i < cantidadElementos)
{
     IDAnterior = items[i]; // condición del corte
     while(i < cantidadElementos && IDActual = IDAnterior)
     {
          // proceso datos
     }
     i++;
}

Puedes descargar una solución de Visual Studio 2005 con el ejemplo (25.93 KB).

}


Jul 25 2008

Obtener información de la pantalla con C# {

Tag: C#, Mini Tips

Más de una vez se puede dar una situación en la que necesitamos ubicar un form en determinada área de la pantalla, o redimensionarlo de acuerdo al tamaño de ésta, para eso necesitamos saber al menos el ancho y alto de nuestra pantalla, o sea la resolución de pantalla.

La función a continuación nos devuelve un String que contiene información útil acerca de la pantalla donde se está ejecutando la aplicación. Tomando esta función como ejemplo, se puede por ejemplo ubicar un form en la esquina inferior derecha de nuestra pantalla.

        public String InfoPantalla(System.Windows.Forms.Control c)
        {
            // \r\n es salto de línea y \t es tabulador
            return String.Format("Es Primario: \t{0}\r\nAncho: \t{1}\r\nAlto: \t{2}\r\nAncho útil: \t{3}\r\nAlto útil: \t{4}",
                System.Windows.Forms.Screen.FromControl(c).Primary, // Devuelve True si se trata del monitor principal del quipo
                System.Windows.Forms.Screen.FromControl(c).Bounds.Width, // Devuelve el ancho total de la pantalla
                System.Windows.Forms.Screen.FromControl(c).Bounds.Height, // Devuelve el alto total de la pantalla
                System.Windows.Forms.Screen.FromControl(c).WorkingArea.Width, // Devuelve el ancho útil de la pantalla, sin contar docks y taskbars
                System.Windows.Forms.Screen.FromControl(c).WorkingArea.Height); // Devuelve el alto útil de la pantalla, sin contar docks y taskbars
        }
    }

La función recibe un objeto Control porque está pensaga para ser implementada en una clase, pero si la misma se implementa en un WindowsForm, o por qué no, un UserControl, Screen.FromControl() puede recibir directamente this como parámetro.

}


Jul 22 2008

Manejo de hilos en C# (parte 1) + delegates + invoke {

Tag: C#

En el artículo que explica la implementación del patrón Singleton en C# utilizamos un método que inicializa 42 hilos los cuales modifican un atributo del objeto Singleton. Además de eso, cuando se lanza el evento que “avisa” de la modificación del atributo, se muestra el valor en una label del form, por lo cual deberemos usar delegados para evitar un problema de CrossThreadException.

Primero veamos el código que crea los threads:

private void button2_Click(object sender, EventArgs e)
        {
            System.Threading.ThreadStart ts = new System.Threading.ThreadStart(sumarRandomico);
 
            Int16 i;
            for (i = 0; i <= 42; i++)
            { 
                System.Threading.Thread t = new System.Threading.Thread(ts);
                t.IsBackground = true;
                t.Name = String.Format("Thread_{0}", i.ToString());
                t.Start();
                System.Diagnostics.Trace.WriteLine(String.Format("Thread {0} >>> Contador: {1} <<<", t.Name, objetoSingleton.Instancia.Contador.ToString()));
            }
        }

En la primera linea creamos el ThreadStart, que es el que indica el método que se ejecutará en el thread, luego dentro del bucle for creamos el objeto thread, seteamos que se ejecute en background, le seteamos un nombre y luego iniciamos la ejecución del hilo. Hasta ahí no hay misterio, simplemente instanciamos e iniciamos varios hilos, en caso de que el método que se ejecutará en el nuevo hilo requiera parámetros, utilizaremos un ParameterizedThreadStart que veremos en la segunda parte de este artículo.

Ahora veamos cómo mostrar en pantalla el valor evitando las CrossThreadException, las mismas suceden cuando se intenta acceder a un objeto de un hilo, (la interfaz en este caso), desde otro hilo.
Cuando se intenta acceder a un control desde otro hilo, la propiedad InvokeRequired toma el valor true, por lo que consultando el valor de esta propiedad sabremos si podemos acceder directamente al control o no.

void Instancia_CambioContador(long nuevoValor)
        {
            if (!this.label2.InvokeRequired)
            {
                CambiarValorLabel(nuevoValor.ToString());
            }
            else
            {
                CambiarValorDelegate del = new CambiarValorDelegate(CambiarValorLabel);
                this.label2.Invoke(del,nuevoValor.ToString());
            }
        }
 
        private delegate void CambiarValorDelegate(String nuevoValor);
        private void CambiarValorLabel(String nuevoValor)
        {
            this.label2.Text = nuevoValor;
        }

Como vemos, si InvokeRequired es false, llamamos directamente al método que modifica el Text de nuestra label, de lo contrario necesitaremos crear un delegate para ese método y utilizarlo con el método Invoke del control.
Un delegate es, a grandes rasgos, un puntero, lo cual en este caso evita la CrossThreadException porque accede directamente a la dirección de memoria del método que llamaremos. Para utilizar un delegate, tenemos que definirlo primero y éste debe tener la misma firma que el método que llamará.

Al momento de utilizarlo, instanciamos nuestro delegate y utilizamos el método Invoke de nuestra label:

CambiarValorDelegate del = new CambiarValorDelegate(CambiarValorLabel);
this.label2.Invoke(del,nuevoValor.ToString());

}


Jul 22 2008

Implementación del patrón Singleton en C# {

Tag: C#, Patrones, Singleton

El patrón Singleton se utiliza cuando es necesario tener una única instancia de una clase para toda la aplicación, ésto se logra otorgando la responsabilidad de crear la instancia a la misma clase. En nuestro ejemplo vemos que el constructor es privado y la propiedad Instancia se encarga de instanciar el objeto, (si éste no fue instanciado antes), y devolver la única instancia.

El código de nuestra clase es el siguiente:

public class objetoSingleton 
{ 
	private DateTime _creacion; 
	private Int64 _contador = 0; 
 
	public event onCambioContador CambioContador; 
	public delegate void onCambioContador(Int64 nuevoValor); 
 
#region Singleton 
	private static Object lockObject = new Object();
	private static objetoSingleton _instancia; 
 
	private objetoSingleton() { 
		this._creacion = DateTime.Now; 
	} 
 
	public static objetoSingleton Instancia 
	{ 
		get 
		{ 
			lock (lockObject) 
			{ 
				if (_instancia == null) 
				{ 
					_instancia = new objetoSingleton(); 
				} 
				return _instancia; 
			} 
		} 
	} 
#endregion 
 
	public DateTime FechaHoraCreacion 
	{ 
		get { return _creacion; } 
	} 
 
	public void SumarContador(Int64 valor) 
	{ 
		_contador += valor; 
		CambioContador(_contador); 
	} 
 
	public Int64 Contador 
	{ 
		get { return _contador; } 
	} 
}

Como se puede ver en el código, al cambiar el valor del contador se lanza un evento, que luego capturaremos para mostrar el valor del contador.
Para probar nuestro Singleton, vamos a usar un form, que podrá acceder directamente al objeto para sumar 1 al contador, podrá además crear 42 hilos que sumarán números aleatorios al contador y también podrá crear instancias de si mismo.

Al final del artículo está el enlace para descargar el proyecto hecho en Visual Studio 2005, además en este post estudiaremos el manejo de hilos, (threads), en C# utilizado en el form.

Descargar el ejemplo de Patrón Singleton en C# (21.22 KB)

}


Página 1 de 11