Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
domingo, 13 de abril de 2008
Piezas de puzzleUna vez visto el concepto de las clases parciales, ya es posible profundizar en los métodos parciales, una característica aparecida en las nuevas versiones de los lenguajes estrella de Microsoft, C# y VB.Net.

Estos métodos, declarados en el contexto de una clase parcial, permiten comunicar de forma segura los distintos fragmentos de dicha clase. De forma similar a los eventos, permiten que un código incluya una llamada a una función que puede (o no) haber sido implementada por un código cliente, aunque en este caso obligatoriamente la implementación se encontrará en uno de los fragmentos de la misma clase desde donde se realiza la llamada.

En la práctica significa que una clase parcial puede declarar un método y utilizarlo (invocarlo) dentro de su código; si el método está implementado en otro fragmento de la clase, se ejecutará como siempre, pero si no ha sido implementado, el código correspondiente a la llamada será eliminado en tiempo compilación para optimizar el resultado... ¡sí, eliminado!

Por ejemplo, el siguiente código muestra una declaración de un método parcial en el interior de una clase, y su utilización desde dentro de uno de sus métodos:
 // C#
public partial class Ejemplo
{

// El método parcial se declara
// sin implementación...

partial void Log(string msg);


public void RealizarAlgo()
{
hacerAlgoComplejo();
Log("¡Ojo!"); // Usamos el método
// parcial declarado antes

}

[...] // Otros métodos y propiedades

}

' VB.NET
Partial Public Class Ejemplo

' El método parcial se declara, sin
' implementar nada en el cuerpo

Partial Private Sub Log(ByVal msg As String)
End Sub


Public Sub RealizarAlgo()
HacerAlgoComplejo()
Log("¡Ojo!") ' Usamos el método parcial
End Sub

[...] ' Otros métodos y propiedades
End Class
 
Y esta la parte realmente curiosa. Cuando el compilador detecta la invocación del método parcial Log(), buscará en todos los fragmentos de la clase a ver si existe una implementación del mismo. Si no existe, eliminará del ensamblado resultante la llamada a dichos métodos, es decir, actuará como si éstas no existieran en el código fuente.

En caso afirmativo, es decir, si existen implementaciones como las del siguiente ejemplo, todo se ejecutará conforme a lo previsto:
 // C#
public partial class Variable
{
partial void Log(string msg)
{
Console.WriteLine(msg);
}
}

' VB.Net
Partial Public Class Variable
Private Sub Log(ByVal msg As String)
Console.WriteLine(msg)
End Sub
End Class
 
Antes comentaba que los métodos parciales son, en cierto sentido, similares a los eventos, pues conceptualmente permiten lo mismo: pasar el control a un código cliente en un momento dado, en el caso de que éste exista. De hecho, hay muchos desarrolladores que lo consideran como un sustituto ligero a los eventos, pues permite prácticamente lo mismo pero se implementan de forma más sencilla.

Existen, sin embargo, algunas diferencias entre ambos modelos, como:
  • Los métodos parciales se implementan en la propia clase, mientras que los eventos pueden ser consumidos también desde cualquier otra
  • El enlace, o la suscripción, a eventos es dinámica, se realiza en tiempo de ejecución, es necesario incluir código para ello; los métodos parciales, sin embargo, se vinculan en tiempo de compilación
  • Los eventos permiten múltiples suscripciones, es decir, asociarles más de un código cliente
  • Los eventos pueden presentar cualquier visibilidad (pública, privada...), mientras que los métodos parciales son obligatoriamente privados.
También puede verse como algo parecido a la herencia en una jerarquía de clases. Una clase puede incluir un método virtual vacío y utilizarlo en su implementación, y si una clase hija sobreescribe el mismo, ésta se ejecutará en su lugar. Pero claro, hay que tener en cuenta que el ámbito de consumo del método parcial es la misma clase donde se declara e implementa.

Por último, es conveniente citar algunas consideraciones sobre los métodos parciales:
  • Deben ser siempre privados (ya lo había comentado antes)
  • No deben devolver valores (en VB.Net serían SUB, en C# serían de tipo void)
  • Pueden ser estáticos (shared en VB)
  • Pueden usar parámetros, acceder a los miembros privados de la clase... en definitiva, actuar como un método más de la misma

En resumen, los métodos parciales forman parte del conjunto de novedades de C# y VB.Net que no son absolutamente necesarias y que a veces pueden parecer incluso diabólicas, pues facilitan la dispersión de código y dificultan la legibilidad. Además, en breve publicaré un post comentando posibles efectos laterales a tener en cuenta cuando usemos los métodos parciales en nuestros desarrollos.

Sin embargo, es innegable que los métodos parciales nos facilitan enormemente la inclusión de código de usuario en el interior de clases generadas de forma automática. Por ejemplo, el diseñador visual del modelo de datos de LinqToSQL genera los DataContext como clases parciales, en las que define un método parcial llamado OnCreated(). Si el usuario quiere incluir alguna inicialización personal al crear los DataContext, no tendrá que tocar el código generado de forma automática; simplemente creará otro fragmento de la clase parcial e implementará este método, de una forma mucho más natural y cómoda que si se tratara de un evento.

Publicado en: http://www.variablenotfound.com/.
jueves, 10 de abril de 2008
Pablo ha lanzado una pregunta en el post Deshabilitar y habilitar un validador ASP.Net desde Javascript publicado hace unos meses, que creo interesante responder en una entrada en exclusiva, por si puede ayudar a alguien más.
"Al utilizar la funcion ValidatorEnable para habilitar un validador, me activa automaticamente la validacion, y me muestra el texto que pongo para cuando la validacion no se cumpla, como puedo evitar esto"

Recordemos que el post trataba sobre cómo conseguir, desde Javascript, habilitar o deshabilitar validadores de controles incluidos en un webform utilizando la función ValidatorEnable(), que pone a nuestra disposición ASP.Net.

El problema, como comenta Pablo, es que al habilitar la validación desde script se muestran de forma automática los mensajes de error en todos aquellos controles que no sean válidos, provocando un efecto que puede resultar desconcertante para el usuario.

Indagando un poco, he comprobado que el problema se debe a que ValidatorEnable(), después de habilitar el validator, comprueba si los valores del control son correctos, mostrando el error en caso contrario.

Existen al menos dos formas de solucionar este problema.

La primera consiste en jugar con la visibilidad del mensaje de error. Como se observa en el siguiente código, al llamar a la función HabilitaValidador(), ésta llamará a ValidatorEnable y acto seguido, si el control no es válido, oculta el mensaje de error:

function HabilitaValidador(validator, habilitar)
{
ValidatorEnable(validator, habilitar);
if (habilitar && !validator.isvalid)
validator.style.visibility = "hidden";
}
 
La segunda forma consiste en simular el comportamiento interno de ValidatorEnable, pero eliminando la llamada a la comprobación de la validez del control.

function HabilitaValidador(validator, habilitar)
{
validator.enabled = habilitar;
}
 
Como se puede ver, simplemente se está estableciendo la propiedad enabled del validador, sin realizar ninguna comprobación posterior.

En ambos casos, la forma de utilizar esta función desde script sería la misma:

function activar()
{
HabilitaValidador("<%= RequiredFieldValidator1.ClientID %>", true);
}
 
Para mi gusto la opción más limpia, aunque sea jugando con la visibilidad de los elementos, es la primera de las mostradas, pues se respeta el ciclo completo de validación. En el segundo método nos estamos saltando las validaciones y el seguimiento de la validez global de la página, que la función original ValidatorEnable sí contempla.

Espero que esto resuelva la duda.

Publicado en: www.variablenotfound.com.
domingo, 6 de abril de 2008
Semanas atrás publicaba el post "101 citas célebres del mundo de la informática", la traducción del post original de Timm Martin en Devtopics, "101 Great computer quotes". El tema me pareció tan divertivo e interesante que he realizado una nueva recopilación de otras tantas frases relacionadas con el mundo de la informática, y con especial énfasis en el desarrollo de software.

Este es el segundo post de la serie compuesta por:
miércoles, 2 de abril de 2008
Piezas de puzzleAunque las clases parciales aparecieron hace unos años, con la llegada de .Net 2.0 y Visual Studio 2005, vamos a hacer un breve repaso como preparación para un próximo post que trate los métodos parciales.

Las clases parciales (llamados también tipos parciales) son una característica presente en algunos lenguajes de programación, como C# y VB.Net, que permiten que la declaración de una clase se realice en varios archivos de código fuente, rompiendo así la tradicional regla "una clase, un archivo". Será tarea del compilador tomar las porciones de los distintos archivos y fundirlas en una única entidad.

En VB.Net y C#, a diferencia de otros lenguajes, es necesario indicar explícitamente que una clase es parcial, es decir, que es posible que haya otros archivos donde se continúe la declaración de la misma, usando en ambos con la palabra clave partial en la definición del tipo:
  ' VB.NET 
Public Partial Class Persona
...
End Class

// C#
public partial class Persona
{
...
}
 
En este código hemos visto cómo se declara una clase parcial en ambos lenguajes, que es prácticamente idéntica salvo por los detalles sintácticos obvios. Por ello, a partir de este momento continuaré introduciendo los ejemplos sólo en C#.

Pero antes un inciso: la única diferencia entre ambos, estrictamente hablando, es que C# obliga a que todas las apariciones de la clase estén marcadas como parciales, mientras que en VB.Net puede dejarse una de ellas (llamémosla "declaración principal") sin indicar que es parcial, y especificarlo en el resto de apariciones. En mi opinión, esta no es una práctica recomendable, por lo que aconsejaría utilizar el modificador partial siempre que la clase lo sea, e independientemente del lenguaje utilizado, pues contribuirá a la mantenibilidad del código.

El número de partes en las que se divide una clase es indiferente, el compilador tomará todas ellas y generará en el ensamblado como si fuera una clase normal.

Para comprobarlo he creado un pequeño código con dos clases exactamente iguales, salvo en su nombre. Una de ellas se denomina PersonaTotal, y está definida como siempre, en un único archivo; la otra, PersonaParcial, es parcial y la he troceado en tres archivos, como sigue:
  // *** Archivo PersonaParcial.Propiedades.cs ***
// Aquí definiremos todas las propiedades

partial class PersonaParcial
{
public string Nombre { get; set; }
public string Apellidos { get; set; }
}

// *** Archivo PersonaParcial.IEnumerable.cs ***
// Aquí implementaremos el interfaz IEnumerable

partial class PersonaParcial: IEnumerable
{
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}

// *** Archivo PersonaParcial.Metodos.cs ***
// Aquí implementaremos los métodos que necesitemos

partial class PersonaParcial
{
public override string ToString()
{
return Nombre + " " + Apellidos;
}
}
 
Y efectivamente, el resultado de compilar ambas clases, según se puede observar con ILDASM es idéntico:

Clases generadas tras la compilación

A la hora de crear clases parciales es conveniente tener los siguientes aspectos en cuenta:
  • Los atributos de la clase resultante serán la combinación de los atributos definidos en cada una de las partes.
  • El tipo base de los distintos fragmentos debe ser el mismo, o aparecer sólo en una de las declaraciones parciales.
  • Si se trata de una clase genérica, los parámetros deben coincidir en todas las partes.
  • Los interfaces que implemente la clase resultante será la unión de todos los implementados por las distintas secciones.
  • De la misma forma, los miembros (métodos, propiedades, campos...) de la clase final será la unión de todos los definidos en las distintas partes.

Vale, ya hemos visto qué son y cómo se usan, pero, ¿para qué sirven? ¿cuándo es conveniente utilizarlas? Pues bien, son varios los motivos de su existencia, algunos discutibles y otros realmente interesantes.

En primer lugar, no era sencillo que varios desarrolladores trabajaran sobre una misma clase de forma concurrente. Incluso utilizando sistemas de control de versiones (como Sourcesafe o Subversion), la unidad mínima de trabajo es el archivo de código fuente, y la edición simultánea podía generar problemas a la hora de realizar fusiones de las porciones modificadas por cada usuario.

En segundo lugar, permite que clases realmente extensas puedan ser troceadas para facilitar su comprensión y mantenimiento. Igualmente, puede utilizarse para separar código en base a distintos criterios:
  • por ejemplo, separar la interfaz (los miembros visibles desde el exterior de la clase) y por otra los miembros privados a la misma
  • o bien separar las porciones que implementan interfaces, o sobreescriben miembros de clases antecesoras de los pertenecientes a la propia clase
  • o separar temas concernientes a distintos dominios o aspectos
En tercer lugar, aunque da la impresión que fue el motivo más importante para decidir su inclusión, las clases parciales permiten utilizar de forma efectiva herramientas automáticas de generación de código.

Así, es posible que un desarrollador y un generador estén introduciendo cambios sobre la misma clase sin molestarse, cada uno jugando con su propia porción de la clase; el primero puede introducir funcionalidades sin preocuparse de que una nueva generación automática de código pueda machacar su trabajo. Visual Studio y otros entornos de desarrollo hacen uso intensivo de esta capacidad, por ejemplo, en los diseñadores visuales de Windows Forms, WPF, ASP.Net e incluso el generador de modelos de LinqToSql.

Publicado en: http://www.variablenotfound.com/.
domingo, 30 de marzo de 2008
MundoVía Dirson me entero de que Google ha publicado recientemente el API que permite, a base de Ajax, realizar traducciones de textos entre los idiomas contemplados por la herramienta, más de una decena.

Google nos tiene acostumbrados a implementar APIs muy sencillas de usar, y en este caso no podía ser menos. Para demostrarlo, vamos a crear una página web con un sencillo traductor en Javascript, comentando paso por paso lo que hay que hacer para que podáis adaptarlo a vuestras necesidades.

Paso 1: Incluir el API

La inclusión de las funciones necesarias para realizar la traducción es muy sencilla. Basta con incluir el siguiente código, por ejemplo, en la sección HEAD de la página:
  <script type="text/javascript"
src="http://www.google.com/jsapi">
</script>
<script type="text/javascript">
google.load("language", "1");
</script>
 
El primer bloque descarga a cliente la librería javascript Ajax API Loader, el cargador genérico de librerías Ajax utilizado por Google. Éste se usa en el segundo bloque script para cargar, llamando a la función google.load el API "language" (traductor), en su versión 1 (el segundo parámetro).

Paso 2: Creamos el interfaz

Nuestro traductor será realmente simple. Además, vamos a contemplar un pequeño subconjunto de los idiomas permitidos para no complicar mucho el código, aunque podéis añadir todos los que consideréis necesarios.

El resultado será como el mostrado en la siguiente imagen.

Interfaz del traductor

El código fuente completo lo encontraréis al final del post, por lo que no voy a repetirlo aquí. Simplemente indicar que tendremos un TextArea donde el usuario introducirá el texto a traducir, dos desplegables con los idiomas origen y destino de la traducción, y un botón que iniciará la acción a través del evento onclick. Por último, se reservará una zona en la que insertaremos el resultado de la traducción.

Ah, un detalle interesante: en el desplegable de idiomas de origen se ha creado un elemento en el desplegable "Auto", cuyo valor es un string vacío; esto indicará al motor de traducción que infiera el idioma a partir del texto enviado.

Paso 3: ¡Traducimos!

La pulsación del botón provocará la llamada a la función Onclick(), desde donde se realizará la traducción del texto introducido en el TextArea.

Como podréis observar en el código, en primer lugar obtendremos los valores de los distintos parámetros, el texto a traducir y los idiomas origen y destino, y lo introducimos en variables para facilitar su tratamiento.
  var text    = document.getElementById("text").value;
var srcLang = document.getElementById("srcLang").value;
var dstLang = document.getElementById("dstLang").value;
 
Acto seguido, realizamos la llamada al traductor. El primer parámetro será el texto a traducir, seguido de los idiomas (origen y destino), y por último se introduce la función callback que será invocada cuando finalice la operación; hay que tener en cuenta que la traducción es realizada a través de una llamada asíncrona a los servidores de Google:

google.language.translate(text, srcLang, dstLang, function(result)
{
if (!result.error)
{
var resultado = document.getElementById("result");
resultado.innerHTML = result.translation;
}
else alert(result.error.message);
}
);
 
Como podéis observar, y es quizás lo único extraño que tiene esta instrucción, el callback se ha definido como una función anónima definida en el espacio del mismo parámetro (podéis ver otro ejemplo de este tipo de funciones cuando explicaba cómo añadir funciones con parámetros al evento OnLoad).

Para los queráis jugar con esto directamente, ahí va el código listo para un copiar y pegar.

<html>
<head>
<title>Traductor</title>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("language", "1");
</script>
</head>

<body>
<textarea id="text" rows="8" cols="40">Hi, how are you?</textarea>
<br />
<button onclick="onClick()">
Traducir</button>
Del idioma
<select id="srcLang">
<option value="">(Auto)</option>
<option value="es">Español</option>
<option value="en">Inglés</option>
<option value="fr">Francés</option>
</select>
Al
<select id="dstLang">
<option value="es">Español</option>
<option value="en">Inglés</option>
<option value="fr">Francés</option>
</select>
<h3>Traducción:</h3>
<div id="result">
(Introduce un texto)
</div>
</body>
<script type="text/javascript">
function onClick()
{
// obtenemos el texto y los idiomas origen y destino
var text = document.getElementById("text").value;
var srcLang = document.getElementById("srcLang").value;
var dstLang = document.getElementById("dstLang").value;

// llamada al traductor
google.language.translate(text, srcLang, dstLang, function(result)
{
if (!result.error)
{
var resultado = document.getElementById("result");
resultado.innerHTML = result.translation;
}
else alert(result.error.message);
}
);
}
</script>
</html>
 


Enlaces: Página oficial de documentación del API

Publicado en: www.variablenotfound.com.