tag:blogger.com,1999:blog-277521262008-05-12T02:03:33.774+02:00Variable not foundJosé M. Aguilarhttp://www.blogger.com/profile/11683750380776435448noreply@blogger.comBlogger157125tag:blogger.com,1999:blog-27752126.post-66513917884735174382008-05-11T22:00:00.002+02:002008-05-11T22:03:35.207+02:00Retornando ActionResults en acciones ASP.NET MVC<a href="http://bp3.blogger.com/_O9D62hXq-ng/SCdPsZqlBqI/AAAAAAAAAb4/_Y_fLjDFSFU/s1600-h/programacion.jpg"><img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_O9D62hXq-ng/SCdPsZqlBqI/AAAAAAAAAb4/_Y_fLjDFSFU/s400/programacion.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5199211919044839074" /></a>Scott Guthrie, en el <a href="http://weblogs.asp.net/scottgu/archive/2008/04/16/asp-net-mvc-source-refresh-preview.aspx">anuncio de la publicación en Codeplex de la actualización del código del framework MVC</a> del pasado mes de abril, comentó que uno de los cambios en los que estaban trabajando era en la modificación de las acciones de un controlador, haciendo que éstas pasaran a retornar un objeto del tipo <em>ActionResult</em>.<br /><br />Como recordaréis, hasta ese momento las acciones no tenían tipo de retorno, pero han replanteado el diseño para hacerlo más flexible, testable y potente. Así, pasamos de definir las acciones de esta forma:<br /><pre><code>public void Index()<br />{<br /> RenderView("Index");<br />}</code></pre> <br />a esta otra:<br /><pre><code>public ActionResult Index() <br />{<br /> return RenderView();<br />}</code></pre> <br />En el código anterior destacan dos aspectos. En primer lugar que la llamada a <code>RenderView()</code> no tiene parámetros; el sistema mostrará la vista cuyo nombre coincida con el de la acción que se está ejecutando (Index, en este caso). En segundo lugar, que la llamada a <code>RenderView()</code> retorna un objeto <code>ActionResult</code> (o más concretamente un descendiente, <code>RenderViewResult</code>), que será el devuelto por la acción.<br /><br />De la misma forma, existen tipos de ActionResult concretos para retornar el resultado de las acciones más habituales:<br /><ul><li><code>RenderViewResult</code>, retornado cuando se llama desde el controlador a <code>RenderView()</code>.</li><li><code>ActionRedirectResult</code>, retornado al llamar a <code>RedirectToAction()</code></li><li><code>HttpRedirectResult</code>, que será la respuesta a un <code>Redirect()</code></li><li><code>EmptyResult</code>, que intuyo que será para casos en los que no hay que hacer nada (!), aunque todavía no le he visto mucho el sentido...</li></ul><br />Además de las citadas anteriormente, una de las ventajas de retornar estos objetos desde los controladores es que podemos crear nuestra clase, siempre heredando de <code>ActionResult</code>, e implementar comportamientos personalizados.<br /><br />Esto es lo que ha hecho el genial Phil Haack en dos ejemplos recientemente publicados en <a href="http://haacked.com/" title="[ING] Haacked, el blog de Phil Haack">su blog</a>.<br /><br />El primero de ellos, publicado en el post "<a href="http://haacked.com/archive/2008/05/10/writing-a-custom-file-download-action-result-for-asp.net-mvc.aspx" title="[ING] Writing A Custom File Download Action Result For ASP.NET MVC">Writing A Custom File Download Action Result For ASP.NET MVC</a>" muestra una implementación de una acción cuya invocación hace que el cliente descargue, mediante un download, el archivo indicado, en su ejemplo, el archivo <acronym title="Cascade Style Sheets">CSS</acronym> de su sitio web:<br /><pre><code>public ActionResult Download() <br />{<br /> return new DownloadResult <br /> { VirtualPath="~/content/site.css", <br /> FileDownloadName = "TheSiteCss.css"<br /> };<br />}<br /></code></pre> <br />La clase <code>DownloadResult</code> una descendiente de <code>ActionResult</code>, en cuya implementación encontraremos, además de la definición de las propiedades <code>VirtualPath</code> y <code>FileDownloadName</code>, la implementación del método <code>ExecuteResult</code>, que será invocado por el framework al finalizar la ejecución de la acción, y donde realmente se realiza el envío al cliente del archivo, con parámetro <em>content-disposition</em> convenientemente establecido:<br /><pre><code>public class DownloadResult : ActionResult <br />{<br /> public DownloadResult() <br /> {<br /> }<br /><br /> public DownloadResult(string virtualPath) <br /> {<br /> this.VirtualPath = virtualPath;<br /> }<br /><br /> public string VirtualPath { get; set; }<br /><br /> public string FileDownloadName { get; set; }<br /><br /> public override void ExecuteResult(ControllerContext context) <br /> {<br /> if (!String.IsNullOrEmpty(FileDownloadName)) {<br /> context.HttpContext.Response.AddHeader("content-disposition", <br /> "attachment; filename=" + this.FileDownloadName)<br /> }<br /> string filePath = context.HttpContext.Server.MapPath(this.VirtualPath);<br /> context.HttpContext.Response.TransmitFile(filePath);<br /> }<br />}<br /></code></pre> <br /><br />El segundo ejemplo, publicado en el post "<a href="http://haacked.com/archive/2008/05/11/delegating-action-result.aspx" title="Delegating Action Result">Delegating Action Result</a>", vuelve a demostrar otro posible uso de los ActionResults creando un nuevo descendiente, <code>DelegatingResult</code>, que puede ser retornado desde las acciones para indicar qué operaciones deben llevarse a cabo por el framework.<br /><br />El siguiente código muestra cómo retornamos un objeto de este tipo, inicializado con una lambda:<br /><pre><code>public ActionResult Hello() <br />{<br /> return new DelegatingResult(context => <br /> {<br /> context.HttpContext.Response.AddHeader("something", "something");<br /> context.HttpContext.Response.Write("Hello World!");<br /> });<br />}</code></pre> <br />Como veremos a continuación, el constructor de este nuevo tipo recibe un parámetro de tipo <code>Action<ControllerContext></code> y lo almacenará de forma local en la propiedad <code>Command</code>, postergando su ejecución hasta más adelante; será la sobreescritura del método <code>ExecuteResult</code> la que ejecutará el comando:<br /><pre><code>public class DelegatingResult : ActionResult <br />{<br /> public Action<ControllerContext> Command { get; private set; }<br /> <br /> public DelegatingResult(Action<ControllerContext> command) <br /> {<br /> this.Command = command;<br /> }<br /><br /> public override void ExecuteResult(ControllerContext context) <br /> {<br /> if (context == null)<br /> throw new ArgumentNullException("context");<br /> <br /> Command(context);<br /> }<br />}<br /></code></pre> <br />Puedes ver el código completo de ambos ejemplos, así como descargar proyectos de prueba en los artículos originales de Phil Haack:<ul><li>"<a href="http://haacked.com/archive/2008/05/10/writing-a-custom-file-download-action-result-for-asp.net-mvc.aspx" title="[ING] Writing A Custom File Download Action Result For ASP.NET MVC">Writing A Custom File Download Action Result For ASP.NET MVC</a>"</li><li>"<a href="http://haacked.com/archive/2008/05/11/delegating-action-result.aspx" title="Delegating Action Result">Delegating Action Result</a>"</li></ul><br />Por último, recordar que todos estos detalles son relativos a la última actualización de la preview de esta tecnología y podrían variar en futuras revisiones.<br /><br />Publicado en: <a href="http://www.variablenotfound.com/">www.variablenotfound.com</a>.José M. Aguilarhttp://www.blogger.com/profile/11683750380776435448noreply@blogger.comtag:blogger.com,1999:blog-27752126.post-42403272324363540922008-05-06T21:55:00.002+02:002008-05-11T22:08:48.810+02:00Fisix Engine, un motor de física en tiempo real para FlashDesde siempre me han fascinado los motores de física en tiempo real, probablemente debido a mi absoluta ignorancia en el tema. De hecho, cada vez que me topo con alguna demostración de uno de ellos, no puedo evitar pasar un buen rato jugando y observando los efectos que de forma tan asombrosa simulan la realidad.<br /><br /><a href="http://flashfisix.com/" title="Fisix">Fisix</a>, por ejemplo, es un motor de física en 2 dimensiones desarrollado en ActionScript 3.0 para desarrolladores de juegos o simuladores en Flash. Aunque en su web podéis encontrar <a href="http://flashfisix.com/engine.asp" title="Ver más demos">más demos</a>, incrusto una aquí para que os hagáis a la idea de lo que puede llegar a conseguirse con esta librería (podéis probar a arrastrar la muñeca por la pantalla o interactuar con los objetos).<br /><div style="text-align: center;"><br /><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="500" height="300" id="driver3" align="middle"><br /><param name="allowScriptAccess" value="sameDomain" /><br /><param name="movie" value="http://flashfisix.com/demos/ragdoll.swf" /><param name="menu" value="false" /><param name="quality" value="high" /><param name="wmode" value="transparent" /><param name="bgcolor" value="#ff9999" /><embed src="http://flashfisix.com/demos/ragdoll.swf" menu="false" quality="high" wmode="transparent" bgcolor="#ff9999" width="500" height="300" name="driver3" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed><br /></object><br /><br /><a href="http://www.fisixengine.com/demos/mona1.html" title="Ver Fisix Engine en la web oficial" style="font-size: 70%;">Ver en la web oficial</a><br /></div><br />Actualmente se encuentra en su versión alfa 0.5, y puede ser utilizado sin coste exclusivamente en proyectos no comerciales; para otros usos hay que contactar con el autor. En cualquier caso, por el tiempo que ha pasado desde su última actualización, parece que no está muy al día. <br /><br />Publicado en: <a href="http://www.variablenotfound.com/">www.variablenotfound.com</a>.José M. Aguilarhttp://www.blogger.com/profile/11683750380776435448noreply@blogger.comtag:blogger.com,1999:blog-27752126.post-28894441845177796932008-05-04T21:58:00.000+02:002008-05-04T21:58:12.459+02:00Cómo describir los elementos de una enumeración usando métodos de extensión y atributos (C# y VB.NET)<img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_O9D62hXq-ng/R8HNWwvIyeI/AAAAAAAAARg/bri51ZijKvk/s320/cscode.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5170639638121466338" />Me he encontrado en el blog de <a href="http://blogs.freshlogicstudios.com/Posts/View.aspx?Id=388f7d39-0b90-43bc-b03a-c1f605dfb499" title="[ING] Enum Values With User Defined Strings, Made Easy">Fresh Logic Studios</a> con un post donde describen una técnica interesante para obtener descripciones textuales de los elementos de una enumeración. De hecho, ya la había visto hace tiempo en <a href="http://blogs.msdn.com/abhinaba/archive/2005/10/21/483337.aspx" title="[ING] C# 3.0: using extension methods for enum ToString">I know the answer</a> como una aplicación de los métodos de extensión para mejorar una solución que aportaba en un <a href="http://blogs.msdn.com/abhinaba/archive/2005/10/20/483000.aspx" title="[ING] C#: Enum and overriding ToString on it">post anterior</a>.<br /><br />Como sabemos, si desde una aplicación queremos obtener una descripción comprensible de un elemento de una enumeración, normalmente no podemos realizar una conversión directa (<code>elemento.ToString()</code>) del mismo, pues obtenemos los nombres de los identificadores usados a nivel de código. La solución habitual, hasta la llegada de C# 3.0 consistía en incluir dentro de alguna clase de utilidad un conversor estático que recibiera como parámetro en elemento de la enumeración y retornara un string, algo así como:<br /><pre><code> <br /> public static string EstadoProyecto2String(EstadoProyecto e)<br /> {<br /> switch (e)<br /> {<br /> case EstadoProyecto.PendienteDeAceptacion:<br /> return "Pendiente de aceptación";<br /> case EstadoProyecto.EnRealizacion:<br /> return "En realización";<br /> case EstadoProyecto.Finalizado:<br /> return "Finalizado";<br /> default:<br /> throw <br /> new ArgumentOutOfRangeException("Error: " + e);<br /> }<br /> }<br /></code></pre> <br />Este método, sin embargo, presenta algunos inconvenientes. En primer lugar, dado el tipado fuerte del parámetro de entrada del método, es necesario crear una función similar para cada enumeración sobre la que queramos realizar la operación. <br /><br />También puede resultar peligroso separar la definición de la enumeración del método que transforma sus elementos a cadena de caracteres, puesto que puede perderse la sincronización entre ambos cuando, por ejemplo, se introduzca un nuevo elemento en ella y no se actualice el método con la descripción asociada.<br /><br />La solución, que como he comentado me pareció muy interesante, consiste en decorar cada elemento de la enumeración con un atributo que describa al mismo, e implementar un método de extensión sobre la clase base <code>System.Enum</code> para obtener estos valores. Veamos cómo.<br /><br />Ah, una cosa más. Aunque los ejemplos están escritos en C#, se puede conseguir exactamente el mismo resultado en VB.NET simplemente realizando las correspondientes adaptaciones sintácticas. Podrás encontrarlo al final del post.<br /><br /><h3>1. Declaración de la enumeración</h3>Vamos a usar el atributo <code>System.ComponentModel.DescriptionAttribute</code>, aunque podríamos usar cualquier otro que nos interese, o incluso <a href="http://msdn.microsoft.com/es-es/library/84c42s56(VS.80).aspx" title="Crear atributos personalizados en MSDN">crear nuestro propio atributo personalizado</a>. El código de definición de la enumeración sería así:<br /><pre><code><br /> using System.ComponentModel;<br /><br /> public enum EstadoProyecto<br /> {<br /> [Description("Pendiente de aceptación")] PendienteDeAceptacion,<br /> [Description("En realización")] EnRealizacion,<br /> [Description("Finalizado")] Finalizado,<br /> [Description("Facturado y cerrado")] FacturadoYCerrado<br /> }<br /></code></pre> <br /><h3>2. Implementación del método de extensión</h3>Ahora vamos a crear el método de extensión (<a href="http://www.variablenotfound.com/2008/02/mtodos-de-extensin-en-c.html" title="Métodos de extensión">¿qué son los métodos de extensión?</a>) que se aplicará a todas las enumeraciones.<br /><br />Fijaos que el parámetro de entrada del método está precedido por la palabra reservada <code>this</code> y el tipo es <code>System.Enum</code>, por lo que será aplicable a cualquier enumeración.<br /><pre><code><br />using System;<br />using System.ComponentModel;<br />using System.Reflection;<br /><br />public static class Utils<br />{<br /> public static string GetDescription(this Enum e)<br /> {<br /> FieldInfo field = e.GetType().GetField(e.ToString());<br /> if (field != null)<br /> {<br /> object[] attribs = <br /> field.GetCustomAttributes(typeof(DescriptionAttribute), false);<br /><br /> if (attribs.Length > 0)<br /> return (attribs[0] as DescriptionAttribute).Description;<br /> }<br /> return e.ToString();<br /> }<br />}<br /></code></pre> <br /><br /><a href="http://bp1.blogger.com/_O9D62hXq-ng/SB4BuK2h3vI/AAAAAAAAAbQ/P3Zo-5Fthr4/s1600-h/EnumWithExtensionMethods.png"><img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_O9D62hXq-ng/SB4BuK2h3vI/AAAAAAAAAbQ/P3Zo-5Fthr4/s400/EnumWithExtensionMethods.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5196592912730808050" /></a>Y voila! A partir de este momento tendremos a nuestra disposición el método <code>GetDescription()</code>, que nos devolverá el texto asociado al elemento de la enumeración; si éste no existe, es decir, si no se ha decorado el elemento con el atributo apropiado, nos devolverá el identificador utilizado.<br /><br />De esta forma eliminamos de un plumazo los dos inconvenientes citados anteriormente: la separación entre la definición de la enumeración y los textos descriptivos, y la necesidad de crear un conversor a texto por cada enumeración que usemos en nuestra aplicación.<br /><br />Y por cierto, el equivalente en VB.NET completo sería:<br /><pre><code><br />Imports System.ComponentModel<br />Imports System.Reflection<br />Imports System.Runtime.CompilerServices<br /><br />Module Module1<br /> Public Enum EstadoProyecto<br /> <Description("Pendiente de aceptación")> PendienteDeAceptacion<br /> <Description("En realización")> EnRealizacion<br /> <Description("Finalizado")> Finalizado<br /> <Description("Facturado y cerrado")> FacturadoYCerrado<br /> End Enum<br /><br /> <Extension()> _<br /> Public Function GetDescription(ByVal e As System.Enum) As String<br /> Dim field As FieldInfo = e.GetType().GetField(e.ToString())<br /> If Not (field Is Nothing) Then<br /> Dim attribs() As Object = _<br /> field.GetCustomAttributes(GetType(DescriptionAttribute), False)<br /> If attribs.Length > 0 Then<br /> Return CType(attribs(0), DescriptionAttribute).Description<br /> End If<br /> End If<br /> Return e.ToString()<br /> End Function<br />End Module<br /></code></pre> <br /><br /><br />Publicado en: <a href="http://www.variablenotfound.com/">www.variablenotfound.com</a>.José M. Aguilarhttp://www.blogger.com/profile/11683750380776435448noreply@blogger.comtag:blogger.com,1999:blog-27752126.post-10878758057129286422008-04-29T22:01:00.000+02:002008-04-29T21:15:10.901+02:00Chuleta de librerías en cliente de ASP.NET AjaxDe nuevo en <a href="http://aspnetresources.com/blog/ms_ajax_cheat_sheets_batch1.aspx">ASP.NET Resources</a> encuentro una magnífica recopilación, en forma de chuletas de consulta rápida, de las librerías javascript disponibles en cliente usando ASP.NET Ajax. Puedes descargar el archivo pulsando sobre la imagen:<br /><br /><a href="http://aspnetresources.com/downloads/ms_ajax_library_cheat_sheets1.zip"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_O9D62hXq-ng/SAnGI0B-KPI/AAAAAAAAAVo/381O0lFFVgg/s400/msajax_cheat_sheet.png" border="0" alt="Descargar compilación en formato .zip" id="BLOGGER_PHOTO_ID_5190897900229372146" /></a><br /><br />El archivo distribuido, un zip, contiene siete chuletas en formato PDF:<ul><li>Extensiones a los tipos <code>String</code> y <code>Object</code></li><li>Extensiones a los tipos <code>Number</code> y <code>Error</code></li><li>Referencia del tipo <code>DomEvent</code></li><li>Extensiones al tipo <code>DomElement</code></li><li>Extensiones a los tipos <code>Date</code> y <code>Boolean</code></li><li>Eventos del ciclo de vida en cliente</li><li>Extensiones al tipo <code>Array</code></li></ul><a href="http://weblogs.asp.net/scottgu/archive/2007/01/10/download-asp-net-ajax-pdf-cheat-sheets.aspx" title="[ING] Blog de ScottGu">ScottGu ya le dio difusión a través de su blog</a> hace más de un año, así que probablemente no sea nada nuevo para muchos, pero para mí ha sido todo un descubrimiento.<br /><br />Publicado en: <a href="http://www.variablenotfound.com/">www.variablenotfound.com</a>.José M. Aguilarhttp://www.blogger.com/profile/11683750380776435448noreply@blogger.comtag:blogger.com,1999:blog-27752126.post-43882914770764252342008-04-27T21:50:00.001+02:002008-04-27T21:51:49.849+02:00Inicializadores de colecciones en C#<a href="http://bp2.blogger.com/_O9D62hXq-ng/R-FiMyDcDQI/AAAAAAAAATY/S7lYsJZlPMk/s1600-h/csharp.jpg"><img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_O9D62hXq-ng/R-FiMyDcDQI/AAAAAAAAATY/S7lYsJZlPMk/s200/csharp.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5179529018186927362" /></a>Días atrás hablaba de las formas de <a href="http://www.variablenotfound.com/2008/03/inicializacin-rpida-de-objetos-en-c-30.html">inicialización de objetos que nos proporcionaban las últimas versiones de C# y VB.Net</a>, que permitían asignar valores a miembros de instancia de forma muy compacta, legible y cómoda.<br /><br />C# 3.0 nos trae otra sorpresa, también relacionada con el establecimiento de valores iniciales de elementos: los inicializadores de colecciones. Aunque esta característica también estaba prevista para VB.Net 9.0, al final fue desplazada a futuras versiones por problemas de tiempo.<br /><br />Para inicializar una colección, hasta ahora era necesario en primer lugar crear la clase correspondiente para, a continuación, realizar sucesivas invocaciones al método <code>Add()</code> con cada uno de los elementos a añadir:<br /><pre><code> List<string> ls = new List<string>();<br /> ls.Add("Uno");<br /> ls.Add("Dos");<br /> ls.Add("Tres");<br /></code></pre> <br />C# 3.0 permite una alternativa mucho más elegante y rápida de codificar, simplemente se introducen los elementos a añadir a la colección entre llaves (como se hacía con los inicializadores de arrays o los nuevos inicializadores de objetos), separados por comas, como en el siguiente ejemplo:<br /><pre><code> List<string> ls = <br /> new List<string>() { "Uno", "Dos", "Tres" };<br /></code></pre> <br />Si desensamblamos el ejecutable resultante, podremos ver que es el compilador el que ha añadido por nosotros los <code>Add()</code> de cada uno de los elementos después de instanciar la colección:<br /><pre><code><br /> newobj instance void class <br /> [mscorlib]System.Collections.Generic.List`1<string>::.ctor()<br /> stloc.s '<>g__initLocal0'<br /> ldloc.s '<>g__initLocal0'<br /> ldstr "Uno"<br /> callvirt instance void class <br /> [mscorlib]System.Collections.Generic.List`1<string>::<strong>Add</strong>(!0)<br /> nop<br /> ldloc.s '<>g__initLocal0'<br /> ldstr "Dos"<br /> callvirt instance void class <br /> [mscorlib]System.Collections.Generic.List`1<string>::<strong>Add</strong>(!0)<br /> nop<br /> ldloc.s '<>g__initLocal0'<br /> ldstr "Tres"<br /> callvirt instance void class <br /> [mscorlib]System.Collections.Generic.List`1<string>::<strong>Add</strong>(!0)<br /> nop<br /></code></pre> <br />Uniendo esto ahora con los <a href="http://www.variablenotfound.com/2008/03/inicializacin-rpida-de-objetos-en-c-30.html">inicializadores de objetos</a> que ya tratamos un post anterior, fijaos en la potencia del resultado:<br /><pre><code> List<Persona> lp = new List<Persona><br /> {<br /> new Persona { Nombre="Juan", Edad=34 },<br /> new Persona { Nombre="Luis", Edad=53 },<br /> new Persona { Nombre="José", Edad=23 }<br /> };<br /></code></pre> <br />Efectivamente, cada elemento es una nueva instancia de la clase Persona, con las propiedades que nos interesan inicializadas de forma directa. De hacerlo con los métodos tradicionales, para conseguir el mismo resultado deberíamos utilizar muuuchas más líneas de código.<br /><br />Otro ejemplo que demuestra aún más la potencia de esta característica:<br /><pre><code> var nums = new SortedList<int, string><br /> {<br /> { 34, "Treinta y cuatro" },<br /> { 12, "Doce" },<br /> { 3, "Tres" }<br /> };<br /> foreach (var e in nums)<br /> Console.WriteLine(e.Key + " " + e.Value);<br /></code></pre> <br />En el código anterior podemos ver, primero, el uso de variables locales de tipo implícito, para ahorrarnos tener que escribir más de la cuenta. En segundo lugar, se muestra cómo se inicializa una colección cuyo método <code>Add()</code> requiere dos parámetros. En el caso de un <code>SortedList<TKey, TValue></code>, su método <code>Add()</code> requiere la clave de ordenación y el valor del elemento.<br /><br />(Obviamente, el resultado de la ejecución del código anterior será la lista ordenada por su valor numérico (Key))<br /><br />En conclusión, se trata de otra de las innumerables ventajas que nos ofrece la nueva versión de C# destinadas a evitarnos pulsaciones innecesarias, y a la que seguro le daremos uso.<br /><br />Publicado en: <a href="http://www.variablenotfound.com/">www.variablenotfound.com</a>.José M. Aguilarhttp://www.blogger.com/profile/11683750380776435448noreply@blogger.comtag:blogger.com,1999:blog-27752126.post-87887508733907541352008-04-22T22:00:00.002+02:002008-04-22T22:00:00.729+02:00Chuleta de operadores estándar de consulta LINQBuscando información sobre los operadores estándar de consulta de Linq, me he topado en <a href="http://www.aspnetresources.com/blog/linq_sqo__cheat_sheet.aspx">ASP.NET Resources</a> con una chuleta (cheat sheet) que nos puede valer para tener siempre a mano una referencia rápida de los mismos, y de paso, adornar alguna pared que tengamos vacía ;-).<br /><br />Puedes descargarla pulsando sobre la imagen:<br /><br /><a href="http://aspnetresources.com/downloads/linq_standard_query_operators.pdf"><img id="BLOGGER_PHOTO_ID_5190887880070670562" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="Descargar archivo PDF" src="http://bp3.blogger.com/_O9D62hXq-ng/SAm9BkB-KOI/AAAAAAAAAVg/seVtif928Os/s400/linq_cheat_sheet.png" border="0" /></a><br /><br />Si quieres leer más sobre estos operadores, puedes probar también en la referencia oficial, <a href="http://download.microsoft.com/download/5/8/6/5868081c-68aa-40de-9a45-a3803d8134b8/Standard_Query_Operators.doc">The .Net Standard Query Operators</a> <span style="font-size:78%;">[ING]</span>, a leer <a href="http://www.microsoft.com/spanish/msdn/articulos/archivo/041206/voices/StandardQuery.mspx" title="Operadores de consulta estándar">este artículo</a> traducido por el maestro <a href="http://geeks.ms/blogs/ohernandez/">Octavio Hernández</a>, profundizar en <a href="http://msdn2.microsoft.com/es-es/library/bb397896.aspx">MSDN</a>, o en otros de los muchos sitios con información relacionada, como la referencia de <a href="http://www.hookedonlinq.com/StandardQueryOperators.ashx">Hooked On Linq</a><span style="font-size:78%;"> [ING]</span>. <br /><br />Publicado en: <a href="http://www.variablenotfound.com/">http://www.variablenotfound.com/</a>.José M. Aguilarhttp://www.blogger.com/profile/11683750380776435448noreply@blogger.comtag:blogger.com,1999:blog-27752126.post-24466539101666616912008-04-20T22:05:00.004+02:002008-04-21T17:43:27.421+02:00Combinando ASP.NET MVC framework y jQuery, paso a paso<a href="http://bp0.blogger.com/_O9D62hXq-ng/SAy2AEB-KTI/AAAAAAAAAWQ/vj2G22mvdtg/s1600-h/jQuery-logo.gif"><img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_O9D62hXq-ng/SAy2AEB-KTI/AAAAAAAAAWQ/vj2G22mvdtg/s400/jQuery-logo.gif" border="0" alt="jQuery" id="BLOGGER_PHOTO_ID_5191724582649604402" /></a>A la vista de la cantidad de posts que se están escribiendo al respecto y del entusiasmo que despierta su utilización, parece claro que jQuery se está erigiendo como un interesantísimo complemento para el framework MVC de Microsoft.<br /><br /><a href="http://jquery.com/" title="jQuery Homepage">jQuery</a>, para que el no haya oído hablar de ella, es una librería Javascript destinada a facilitar enormemente la vida a los desarrolladores simplificando y unificando el manejo de eventos, la manipulación del contenido (DOM), estilos, el uso de Ajax, la creación de animaciones y efectos gráficos, y un larguísimo etcétera propiciado por la facilidad para añadirle <a href="http://plugins.jquery.com/" title="Repositorio de plugins de jQuery">plugins</a> que extienden sus funcionalidades iniciales. Y todo ello de forma muy rápida, sin excesivas complicaciones, y sin añadir demasiado peso a las páginas.<br /><br />En este post vamos a ver un ejemplo de integración de jQuery con ASP.NET MVC framework realizando una aplicación muy sencilla e ilustrativa que nos enseñará cómo enviar información desde el cliente al servidor y actualizar porciones de página completas con el retorno de éste, respetando en todo momento la filosofía <a href="http://es.wikipedia.org/wiki/Modelo_Vista_Controlador" title="Más sobre Modelo-Vista-Controlador">MVC</a>.<br /><br />El funcionamiento será realmente simple: el usuario introduce su nombre y edad, y al pulsar el botón se enviará esta información al servidor, que la utiliza para componer una respuesta y mandarla de vuelta al cliente. Cuando éste la recibe, la mostrará (con un poco de 'magia' visual de jQuery) y transcurridos unos segundos, desaparecerá de forma automática. La siguiente captura muestra el sistema que vamos a construir en ejecución:<br /><div style="text-align: center"><br /><a href="http://bp2.blogger.com/_O9D62hXq-ng/SAyk8kB-KSI/AAAAAAAAAWI/xslXJot-CvY/s1600-h/MVC-jQuery.png"><img style="text-align: center; cursor: pointer; cursor:hand;" src="http://bp2.blogger.com/_O9D62hXq-ng/SAyk8kB-KSI/AAAAAAAAAWI/xslXJot-CvY/s400/MVC-jQuery.png" border="0" alt="MVC-jQuery en ejecución" id="BLOGGER_PHOTO_ID_5191705830822390050" /></a><br /></div><br />Pero antes de entrar en faena, unos comentarios. En primer lugar, sólo voy a explicar los aspectos de interés para la realización del ejemplo, partiendo de las <a href="http://www.variablenotfound.com/2008/03/probando-aspnet-mvc-preview-2-con-web.html" title="Plantillas para WDExpress 2008 de ASP.NET MVC">plantillas adaptadas para Web Developer Express 2008</a>. Si prefieres antes una introducción sobre el framework, puedes visitar las magníficas traducciones de <a href="http://thinkingindotnet.wordpress.com/">Thinking in .Net</a> de los <a href="http://thinkingindotnet.wordpress.com/2007/11/18/aspnet-mvc-framework-primera-parte/" title="ASP.NET MVC Framework, primera parte">tutoriales de Scott Guthrië sobre MVC</a>. Se refieren a la primera preview, pero los fundamentos son igualmente válidos.<br /><br />Segundo, supongo que funcionará con versiones superiores de Visual Studio, pero no he podido comprobarlo. Está creado y comprobado con Visual Web Developer Express, y la Preview 2 del framework MVC.<br /><br />Y por último, decir que el ejemplo completo podrás descargarlo usando el enlace que encontrarás al final del post. :-)<br /><br /><h3>Primero: Estructuramos la solución</h3>En líneas generales, nuestra aplicación tendrá los siguientes componentes:<ul><li>Tendremos un controlador principal, llamado <em>Home</em>. En él crearemos dos acciones, las dos únicas que permite nuestra aplicación: una, llamada <em>Index</em>, que se encargará de mostrar la página inicial del sistema, y otra llamada <em>Welcome</em>, que a partir de los datos introducidos por el usuario maquetará el interfaz del mensaje de saludo.</li><br /><li>Como consecuencia del punto anterior, dispondremos de dos vistas. La primera, <em>Index</em> compondrá la interfaz principal con el formulario, y la segunda, que llamaremos <em>Welcome</em>, que define la interfaz del saludo al usuario (el recuadro de color amarillo chillón ;-)).<br /><br />Esta última vista necesitará los datos de la persona (nombre y edad) para poder mostrar correctamente su mensaje, por lo que el controlador deberá enviárselos después de obtenerlos de los parámetros de la petición.<br />Fijaos que respetamos en todo momento el patrón MVC haciendo que el cliente invoque a la acción <em>Welcome</em> del controlador usando Ajax, y que la composición del interfaz (HTML) se realice a través de la vista correspondiente. Utilizaremos, por tanto, toda la infraestructura del framework MVC, sin modificaciones.</li><br /><li>También, para añadir algo de emoción, he incluido una página maestra, que definirá el interfaz general de las páginas del sistema y realizará la inclusión de los archivos adicionales necesarios, como las hojas de estilo y scripts como jQuery.</li></ul><h3>Segundo: implementamos el controlador</h3>El controlador de nuestra aplicación va a ser bien simple. Lo vemos y comentamos seguidamente:<br /><pre><code>public class HomeController : Controller<br />{<br /> public void Index()<br /> {<br /> RenderView("Index");<br /> }<br /><br /> public void Welcome(string name, int age)<br /> {<br /> Person person = <br /> new Person { Age = age, Name = name };<br /><br /> RenderView("Welcome", person);<br /> }<br />}<br /><br />public class Person<br />{<br /> public string Name { get; set; }<br /> public int Age { get; set; }<br />}</code></pre> <br />Podemos observar la clase <code>HomeController</code> que implementa las acciones del controlador <em>Home</em>. La acción <em>Index</em> provoca la visualización de la vista de su mismo nombre. <br /><br />La acción <em>Welcome</em>, es algo más compleja; en primer lugar, se observa que recibe dos parámetros, en nombre y edad del usuario, que utiliza para montar un objeto de tipo <code>Person</code>, definido algo más abajo, que posteriormente envía a la vista a la hora de renderizarla. Habréis observado aquí la utilización de <a href="http://www.variablenotfound.com/2008/03/inicializacin-rpida-de-objetos-en-c-30.html" title="Qué son los inicializadores de objetos">inicializadores de objetos</a> en la instanciación, y de <a href="http://www.jasoft.org/blog/PermaLink,guid,87970f6c-f9a7-49a9-b5ce-a38ee86854f6.aspx" title="Qué son las propiedades automáticas">propiedades automáticas</a> en la definición del tipo.<br /><br /><h3>Tercero: implementamos las vistas</h3>Recordemos que vamos a implementar dos vistas, una para la página principal (llamada Index), que mostrará el formulario de entrada de datos, y otra (que llamaremos Welcome) que definirá el interfaz de la respuesta del servidor. Comenzaremos describiendo cómo incluir jQuery en nuestras páginas, y pasaremos después a ellas.<br /><h4>3.1. Inclusión de jQuery</h4>Para implementar las vistas necesitamos antes preparar la infraestructura. En primer lugar, <a href="http://jquery.com/">descargamos jQuery</a> desde la web oficial del proyecto, y la introducimos en nuestro proyecto. Si vas a descargar la solución completa desde aquí, en ella ya viene incluido el archivo.<br /><br />A continuación, es un buen momento para modificar la página maestra e incluir en ella la referencia a esta librería:<br /><pre><code> <script type="text/javascript" <br /> src="/scripts/jquery-1.2.3.min.js"><br /> </script></code></pre> <br />Un inciso importante aquí. Hace unas semanas se publicó un <a href="http://thinkingindotnet.wordpress.com/2008/02/09/vs-2008-web-development-hot-fix-roll-up/" title="Más información sobre el hotfix">HotFix para Visual Studio 2008 y Web Developer Express</a> que corrije, entre otras, deficiencias en el <em>intellisense</em> y hacen posible el uso de esta magnífica ayuda cuando escribimos código jQuery. Altamente recomendable, pues, <a href="https://connect.microsoft.com/VisualStudio/Downloads/DownloadDetails.aspx?DownloadID=10826" title="Descargar el hotfix">instalarse esta actualización</a>.<br /><br />Sin embargo, el hecho de introducir en la página maestra la referencia a la librería jQuery hace que el <em>intellisense</em> no funcione como debe. Por tanto, aunque no es la opción que he elegido en este proyecto, podríais incluir el script directamente en la vista Index en lugar de en la Master, y así disfrutaréis del soporte a la codificación.<br /><h4>3.2. La vista "Index"</h4>Esta vista será la encargada de mostrar el formulario e implementar la lógica de cliente necesaria para obtener los datos del usuario, enviarlos al servidor y actualizar el interfaz con el retorno. Su implementación se encuentra en el archivo <em>Index.aspx</em>.<br /><br />Desde el punto de vista del interfaz de usuario es bastante simple, lo justo para mostrar un par de inputs con sus correspondientes etiquetas y el botón que iniciará la fiesta llamando a la función <code>send()</code>:<br /><pre><code><form action="" id="myForm"><br /> <label for="name">Name: </label><input type="text" id="name" /><br /> <br /><br /> <label for="age">Age: </label><input type="text" id="age" size="2" /><br /> <br /><br /> <button id="btn" onclick="send(); return false;">Send!</button><br /></form><br /><hr /><br /><div id="result" style="display: none; width: 400px"></div></code></pre> <br />Observad que no es necesario establecer un <em>action</em> en el formulario, ni otros de los atributos habituales, pues éste no se enviará (de hecho, el formulario incluso sería innecesario). Fijaos también que el evento <code>onclick</code> del botón retorna <em>false</em>, para evitar que se produzca un <em>postback</em> (¡aaargh, palabra maldita! ;-)) del formulario completo.<br /><br />Puede verse también un <code>div</code> llamado "result", inicialmente invisible, que se utilizará de contenedor para mostrar las respuestas obtenidas desde el servidor.<br /><br />Pasemos ahora al script. La función <code>send()</code> invocada como consecuencia de la pulsación del botón pinta así:<br /><pre><code> function send()<br /> {<br /> var name = document.getElementById("name").value;<br /> var age = document.getElementById("age").value;<br /> updateServerText(name, age);<br /> }</code></pre> <br />En realidad no hace gran cosa: obtiene el valor de los textboxes y llama a la función que realmente hace el trabajo duro. Este hubiera sido un buen sitio para poner validadores, pero eso os lo dejo de deberes ;-).<br /><br />El código de la función <code>updateServerText()</code> es el siguiente:<br /><pre><code> function updateServerText(name, age)<br /> { <br /> document.getElementById("btn").disabled = true;<br /><br /> $.ajax({<br /> cache: false,<br /> url: '<%= Url.Action("Welcome", "Home") %>',<br /> data: {<br /> Name: name, <br /> Age: age <br /> },<br /> success: function(msg) {<br /> $("#result").html(msg).show("slow");<br /> },<br /> error: function(msg) {<br /> $("#result").html("Bad parameters!").show("slow");<br /> }<br /> });<br /> <br /> setTimeout(function () {<br /> document.getElementById("btn").disabled = false;<br /> $("#result").hide("slow");<br /> }, 3000);<br /> }</code></pre> <br />En primer lugar, se desactiva el botón de envío para evitar nuevas pulsaciones hasta que nos interese. He utilizado un método habitual del DOM, <code>getElementById()</code> para conseguirlo, no encontré una alternativa mejor en jQuery.<br /><br />A continuación se utiliza el método <code>ajax</code> de jQuery para realizar la llamada al servidor. Aunque existen otras alternativas de más alto nivel y por tanto más fáciles de utilizar, elegí esta para tener más control sobre lo que envío, la forma de hacerlo y la respuesta. <br /><br />Los parámetros utilizados en la llamada a <code>$.ajax</code> son:<ul><li><code>cache</code>, con el que forzamos la anulación de caché, obligando a que cada llamada se ejecute totalmente, sin utilizar el contenido almacenado en cliente. Internamente, jQuery añade al <em>querystring</em> un parámetro aleatorio, con lo que consigue que cada llamada sea única.</li><br /><li><code>url</code>, la dirección de invocación de la acción, que se genera utilizando el método de ayuda <code>Url.Action()</code>, pasándole como parámetros el controlador y la acción, lo que retornará la URL asociada en el sistema de rutas definido. En condiciones normales, si la aplicación se ejecuta sobre el raíz del servidor web, se traducirá por '/Home/Welcome'.</li><br /><li><code>data</code>, los datos a enviar, que se establecen en formato JSON, donde cada propiedad va seguida de su valor. jQuery tomará estos valores y los transformará en los parámetros que necesita la acción Welcome, por lo que el nombre de las propiedades deberá corresponder con los parámetros que espera esta acción (Name y Age).</li><br /><li><code>sucess</code> define la función de retorno exitoso, que mostrará los datos recibidos del servidor introduciéndolos en el contenedor "result". Y ya que estamos, gracias a la magia de jQuery, se mostrará con un efecto visual muy majo.</li><br /><li><code>error</code>, define una función de captura de errores para casos extraños, por si todo falla. Por ejemplo, dado que no estamos validando la entrada del usuario, si éste introduce texto en la edad, el framework no será capaz de realizar la conversión para pasarle los parámetros al controlador y fallará estrepitosamente; en este caso simplemente mostraremos un mensaje de error en cliente.</li></ul><br />Fijaos que la llamada a la acción (Welcome) del controlador (Home) es capturada por el framework y dirigida al método correspondiente sin necesidad de hacer nada más, dado que se trata de una llamada HTTP normal. De hecho, si sobre el navegador se introduce la dirección "http://localhost:[tupuerto]/Home/Welcome?Name=Peter&Age=12" podremos ver en pantalla el mismo resultado que recibirá la llamada Ajax.<br /><br /><a href="http://bp1.blogger.com/_O9D62hXq-ng/SAJX2xqKexI/AAAAAAAAAVY/QLbzpjdFlyE/s1600-h/DirectQuery.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_O9D62hXq-ng/SAJX2xqKexI/AAAAAAAAAVY/QLbzpjdFlyE/s400/DirectQuery.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5188806319238970130" /></a><br /><br />Obviamente, este efecto podría ser controlado y hacer que sólo se respondieran peticiones originadas a través de Ajax y similares.<br /><br />Por último, continando con el código anterior, dejamos programado un timer para que unos segundos más tarde, el mensaje mostrado, sea cual sea el resultado de la llamada Ajax, desaparezca lentamente y, de paso, se active de nuevo el botón de envío. El efecto, ya lo veréis si ejecutáis la solución, es de lo más vistoso, creando una sensación de interactividad y dinamismo muy a lo 2.0 que está tan de moda.<br /><h4>3.3. La vista "Welcome"</h4>En esta vista definiremos el interfaz del mensaje que mostraremos al usuario cuando introduzca su información y pulse el botón de envío. Dado que estamos usando MVC, la llamada Ajax descrita anteriormente llegará al controlador y éste hará que la vista cree el interfaz que será devuelto al cliente.<br /><br />La vista, por tanto, es como cualquier otra, salvo algunas diferencias interesantes. Por ejemplo, no tiene página maestra, no la necesita; de hecho ni siquiera tiene la estructura de una página HTML completa, sólo de la porción que necesita para montar su representación. El código de <em>Welcome.aspx</em>, salvo las obligatorias directivas iniciales, es:<br /><pre><code><div style="background-color: Yellow; border: 1px solid black;"><br /> <em>Message from server (<%=DateTime.Now %>):</em><br /><br /> Hi, <%= ViewData.Name %>, your age is <%= ViewData.Age %><br /></div></code></pre> <br />Pero aún queda un detalle que afinar. En el archivo code-behind (o codefile) donde se define la clase <code>Welcome</code> hay que indicar expresamente que la clase es una vista de un tipo concreto de la siguiente forma:<pre><code> public partial class Views_WebParts_Welcome : ViewPage<Person><br /> {<br /> }</code></pre> <br />De esta forma indicamos que los datos de la vista son del tipo <code>Person</code>, lo que nos permite beneficiarnos del tipado fuerte en la composición de la misma; de hecho, esto es lo que permite que podamos usar tan alegremente una expresión como <code>ViewData.Age</code> a la hora de componer el interfaz.<br /><br />Fijaos que aunque en este ejemplo no hemos hecho ninguna composición compleja y los datos que hemos usado, contenidos en <code>ViewData</code>, han sido obtenidos por el Controlador directamente de la vista, sería exactamente igual si se tratara de algo menos simple, como una página concreta de datos obtenidos desde el Modelo, por ejemplo con Linq, y mostrados en forma de grid. <br /><br /><h3>Cuarto: recapitulamos</h3>Hemos visto, paso a paso, un ejemplo de cómo podemos utilizar el framework MVC de Microsoft para el desarrollo de aplicaciones web que hacen uso de Ajax, utilizando para ello la excelente librería de scripting jQuery.<br /><br />Para ello hemos creado una vista que es la página Web con el formulario principal, y otra vista parcial con el fragmento compuesto por el servidor con la información recibida. El controlador, por su parte, incluye acciones para responder a las peticiones del cliente independientemente de si se originan a través de Ajax o mediante la navegación del usuario, mostrando la vista oportuna.<br /><br />La modificación dinámica del interfaz, así como las llamadas asíncronas al servidor, encajan perfectamente en la filosofía MVC teniendo en cuenta algunas reglas básicas, como el respeto a las responsabilidades de cada capa.<br /><br />Y ahora, lo prometido:<br /><br /> <a href="http://www.snapdrive.net/files/415885/MVC-JQuery.zip" title="Descargar proyecto MVC-jQuery"><img src="http://bp1.blogger.com/_O9D62hXq-ng/R9vMeYH7nuI/AAAAAAAAATQ/NRq-6LbxFKQ/s200/zip.gif" alt="Descargar el proyecto" style="border: 0"> Descargar proyecto (Visual Web Developer Express 2008)</a>.<br /><br />Publicado en: <a href="http://www.variablenotfound.com/">www.variablenotfound.com</a>.José M. Aguilarhttp://www.blogger.com/profile/11683750380776435448noreply@blogger.comtag:blogger.com,1999:blog-27752126.post-24942376843306816832008-04-15T21:15:00.003+02:002008-04-15T21:20:37.323+02:00Efectos laterales en métodos parciales<a href="http://bp1.blogger.com/_O9D62hXq-ng/R_u3ACDcDaI/AAAAAAAAAUw/8Uhf_ka6jtc/s1600-h/sorprendido.jpg"><img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_O9D62hXq-ng/R_u3ACDcDaI/AAAAAAAAAUw/8Uhf_ka6jtc/s320/sorprendido.jpg" border="0" alt="¡Sorpresa!" id="BLOGGER_PHOTO_ID_5186940607026433442" /></a>Hace unos días comentaba que el uso de <a href="http://www.variablenotfound.com/2008/04/mtodos-parciales-en-c-3-y-vbnet-9.html" title="Métodos parciales en C# y VB.Net">métodos parciales</a> puede causar algunos problemas en la ejecución de nuestras aplicaciones que podríamos calificar, cuanto menos, de "incómodos".<br /><br />Recordemos que una parte de 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, <em>el compilador eliminará tanto la declaración del método como las llamadas que se hagan al mismo</em>.<br /><br />Sin embargo esta eliminación pueden causar efectos no deseados difíciles de detectar.<br /><br />Veámoslo con un ejemplo. Supongamos una clase parcial como la siguiente, que representa a una variable de tipo entero que puede ser incrementada o decrementada a través de métodos:<br /><pre><code> public partial class Variable<br /> {<br /> <strong>partial void Log(string msg);</strong><br /> private int i = 0;<br /><br /> public void Inc()<br /> {<br /> i++;<br /> <strong>Log("Incremento. I: " + i);</strong><br /> }<br /><br /> public void Dec()<br /> {<br /> <strong>Log("Decremento. I: " + (--i));</strong><br /> }<br /><br /> public int Value<br /> {<br /> get { return i; }<br /> }<br /> }<br /></code></pre> <br />Creamos ahora un código que utiliza esta clase de forma muy simple: crea una variable, la incrementa dos veces, la decrementa una vez y muestra el resultado:<br /><pre><code> Variable v = new Variable();<br /> v.Inc();<br /> v.Inc();<br /> v.Dec();<br /> Console.WriteLine(v.Value);<br /></code></pre> <br />Obviamente, tras la ejecución de este código la pantalla mostrará por consola un "1", ¿no? Claro, el resultado de realizar dos incrementos y un decremento sobre el cero.<br /><br />Pues no necesariamente. De hecho, es imposible conocer, a la vista del código mostrado hasta ahora, cuál será el resultado mostrado por consola al finalizar la ejecución. Dependiendo de la existencia de la implementación del método parcial <code>Log()</code>, declarado e invocado en la clase <code>Variable</code> anterior, puede ocurrir:<br /><ul><li>Si existe otra porción de la misma clase (otra <code>partial class Variable</code>) donde se implemente el método, se ejecutará éste. El valor mostrado por consola, salvo que desde esta implementación se modificara el valor del campo privado, sería "1".<br /><br /></li><li>Si no existe una implementación del método <code>Log()</code> en la clase, el compilador <em>eliminará</em> todas las llamadas al mismo. Pero si observáis, esto incluye el decremento del valor interno, que estaba en el interior de la llamada como un autodecremento:<br /><pre><code>Log("Decremento. I: " + <strong>(--i)</strong>);</code></pre>Por tanto, en este caso, el compilador eliminará tanto la llamada a <code>Log()</code> como la operación que se realiza en el interior. Obviamente, el resultado de ejecución de la prueba anterior sería "2". <br /><br />Lo mismo ocurriría si el resultado a mostrar fuera el valor de retorno de una llamada a otra función: esta no se ejecutaría, lo cual puede ser grave si en ella se realiza una operación importante, como por ejemplo:<br /><pre><code>public function InicializaValores()<br />{<br /> Log("Elementos reestablecidos: " + reseteaElementos() );<br />}</code></pre> <br /></li></ul>Conclusión: usad los métodos parciales siempre con precaución, pues podemos introducir errores muy difíciles de detectar y corregir, sobre todo por falta aparente de relación entre causa y efecto. <br /><br />Por ejemplo, imaginad que el ejemplo anterior contiene una implementación de <code>Log()</code>; la aplicación funcionaría correctamente. Sin embargo, si pasado un tiempo se decide eliminar esta implementación (por ejemplo, porque ya no es necesario registrar las operaciones realizadas), la operación de decremento (<code>Dec()</code>) dejaría de funcionar. <br /><br />Aunque, eso sí, no es nada que no se pueda solucionar con un buen juego de pruebas...<br /><br />Publicado en: <a href="http://www.variablenotfound.com/">http://www.variablenotfound.com/</a>.José M. Aguilarhttp://www.blogger.com/profile/11683750380776435448noreply@blogger.comtag:blogger.com,1999:blog-27752126.post-13317948510429496232008-04-13T21:10:00.000+02:002008-04-13T21:11:57.250+02:00Métodos parciales en C# 3 y VB.NET 9<a href="http://bp3.blogger.com/_O9D62hXq-ng/R-KyJyDcDSI/AAAAAAAAATw/Gfz7uODxBoI/s1600-h/puzzle.png"><img id="BLOGGER_PHOTO_ID_5179898402554252578" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; CURSOR: hand" alt="Piezas de puzzle" src="http://bp3.blogger.com/_O9D62hXq-ng/R-KyJyDcDSI/AAAAAAAAATw/Gfz7uODxBoI/s200/puzzle.png" border="0" /></a>Una vez visto el concepto de las <a href="http://www.variablenotfound.com/2008/04/clases-parciales-en-c-y-vbnet.html">clases parciales</a>, 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.<br /><br />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.<br /><br />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... <em>¡sí, eliminado!</em><br /><br />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:<br /><pre><code> <span style='color: green;'>// C#</span><br /> public partial class Ejemplo<br /> {<br /> <strong> <br /> <span style='color:green'>// El método parcial se declara<br /> // sin implementación...</span><br /> partial void Log(string msg);</strong><br /><br /> public void RealizarAlgo()<br /> {<br /> hacerAlgoComplejo();<br /> <strong>Log("¡Ojo!"); <span style='color:green'>// Usamos el método<br /> // parcial declarado antes</span></strong><br /> }<br /> <br /> [...] <span style='color:green'>// Otros métodos y propiedades</span><br /><br /> }<br /><br /> <span style='color: green'>' VB.NET</span><br /> Partial Public Class Ejemplo<br /><br /> <strong><span style='color: green'>' El método parcial se declara, sin<br /> ' implementar nada en el cuerpo</span><br /> Partial Private Sub Log(ByVal msg As String)<br /> End Sub</strong><br /><br /> Public Sub RealizarAlgo()<br /> HacerAlgoComplejo()<br /> <strong>Log("¡Ojo!") <span style='color: green'>' Usamos el método parcial</span></strong><br /> End Sub<br /><br /> [...] <span style='color:green'>' Otros métodos y propiedades</span><br /> End Class<br /></code></pre> <br />Y esta la parte realmente curiosa. Cuando el compilador detecta la invocación del método parcial <code>Log()</code>, 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, <em>actuará como si éstas no existieran en el código fuente</em>.<br /><br />En caso afirmativo, es decir, si existen implementaciones como las del siguiente ejemplo, todo se ejecutará conforme a lo previsto:<br /><pre><code> <span style='color: green'>// C#</span><br /> public partial class Variable<br /> {<br /> partial void Log(string msg)<br /> {<br /> Console.WriteLine(msg);<br /> }<br /> }<br /><br /> <span style='color: green'>' VB.Net</span><br /> Partial Public Class Variable<br /> Private Sub Log(ByVal msg As String)<br /> Console.WriteLine(msg)<br /> End Sub<br /> End Class<br /></code></pre> <br />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. <br /><br />Existen, sin embargo, algunas diferencias entre ambos modelos, como:<br /><ul><li>Los métodos parciales se implementan en la propia clase, mientras que los eventos pueden ser consumidos también desde cualquier otra</li><li>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</li><li>Los eventos permiten múltiples suscripciones, es decir, asociarles más de un código cliente</li><li>Los eventos pueden presentar cualquier visibilidad (pública, privada...), mientras que los métodos parciales son obligatoriamente privados.</li></ul>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.<br /><br />Por último, es conveniente citar algunas consideraciones sobre los métodos parciales:<br /><ul><li>Deben ser siempre privados (ya lo había comentado antes)</li><li>No deben devolver valores (en VB.Net serían <code>SUB</code>, en C# serían de tipo <code>void</code>)</li><li>Pueden ser estáticos (shared en VB)</li><li>Pueden usar parámetros, acceder a los miembros privados de la clase... en definitiva, actuar como un método más de la misma</li></ul><br />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.<br /><br />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 <code>DataContext</code> como clases parciales, en las que define un método parcial llamado <code>OnCreated()</code>. 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.<br /><br />Publicado en: <a href="http://www.variablenotfound.com/">http://www.variablenotfound.com/</a>.José M. Aguilarhttp://www.blogger.com/profile/11683750380776435448noreply@blogger.comtag:blogger.com,1999:blog-27752126.post-80039211927369884982008-04-10T18:15:00.001+02:002008-04-10T18:16:47.720+02:00Ocultar el texto de validadores en javascript (ASP.Net)<a href="http://bp2.blogger.com/_O9D62hXq-ng/R_3hxK3DmFI/AAAAAAAAAVI/lecw8lWubVE/s1600-h/pregunta.png"><img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_O9D62hXq-ng/R_3hxK3DmFI/AAAAAAAAAVI/lecw8lWubVE/s320/pregunta.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5187550580645795922" /></a>Pablo ha lanzado una pregunta en el post <a href="http://www.variablenotfound.com/2007/09/deshabilitarhabilitar-un-validador.html">Deshabilitar y habilitar un validador ASP.Net desde Javascript</a> publicado hace unos meses, que creo interesante responder en una entrada en exclusiva, por si puede ayudar a alguien más.<br /><blockquote>"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"</blockquote><br />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 <code>ValidatorEnable()</code>, que pone a nuestra disposición ASP.Net.<br /><br />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.<br /><br />Indagando un poco, he comprobado que el problema se debe a que <code>ValidatorEnable()</code>, después de habilitar el validator, comprueba si los valores del control son correctos, mostrando el error en caso contrario.<br /><br />Existen al menos dos formas de solucionar este problema.<br /><br />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 <code>HabilitaValidador()</code>, ésta llamará a <code>ValidatorEnable</code> y acto seguido, si el control no es válido, oculta el mensaje de error:<br /><pre><code><br /> function HabilitaValidador(validator, habilitar)<br /> {<br /> ValidatorEnable(validator, habilitar);<br /> if (habilitar && !validator.isvalid)<br /> validator.style.visibility = "hidden";<br /> }<br /></code></pre> <br />La segunda forma consiste en simular el comportamiento interno de <code>ValidatorEnable</code>, pero eliminando la llamada a la comprobación de la validez del control. <br /><pre><code><br /> function HabilitaValidador(validator, habilitar)<br /> {<br /> validator.enabled = habilitar;<br /> }<br /></code></pre> <br />Como se puede ver, simplemente se está estableciendo la propiedad <code>enabled</code> del validador, sin realizar ninguna comprobación posterior.<br /><br />En ambos casos, la forma de utilizar esta función desde script sería la misma:<br /><pre><code><br /> function activar()<br /> { <br /> HabilitaValidador("<%= RequiredFieldValidator1.ClientID %>", true);<br /> }<br /></code></pre> <br />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 <code>ValidatorEnable</code> sí contempla.<br /><br />Espero que esto resuelva la duda.<br /><br />Publicado en: <a href="http://www.variablenotfound.com/">www.variablenotfound.com</a>.José M. Aguilarhttp://www.blogger.com/profile/11683750380776435448noreply@blogger.comtag:blogger.com,1999:blog-27752126.post-64607921947863752562008-04-06T22:00:00.008+02:002008-04-06T22:17:20.472+02:00Otras 101 citas célebres del mundo de la informática<a href="http://bp3.blogger.com/_O9D62hXq-ng/R_fsFiDcDZI/AAAAAAAAAUo/PKDdn0kL7LY/s1600-h/pensador.png"><img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_O9D62hXq-ng/R_fsFiDcDZI/AAAAAAAAAUo/PKDdn0kL7LY/s320/pensador.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5185873075725143442" /></a>Semanas atrás publicaba el post "<a href="http://www.variablenotfound.com/2008/02/101-citas-clebres-del-mundo-de-la.html">101 citas célebres del mundo de la informática</a>", la traducción del post original de Timm Martin en <a href="http://www.devtopics.com/">Devtopics</a>, "<a href="http://www.devtopics.com/101-great-computer-programming-quotes/">101 Great computer quotes</a>". 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.<br /><br /><h3>Informática</h3>1. "No temo a los ordenadores; lo que temo es quedarme sin ellos"<br /><em> -- Isaac Asimov</em><br /><br />2. "Una vez un ordenador me venció jugando al ajedrez, pero no me opuso resistencia cuando pasamos al kick boxing"<br /><em> -- Emo Philips</em><br /><br />3. "La informática tiene que ver con los ordenadores lo mismo que la astronomía con los telescopios"<br /><em> -- Edsger W. Dijkstra</em><br /><br />4. "El ordenador nació para resolver problemas que antes no existían"<br /><em> -- Bill Gates</em><br /><br />5. "El software es como la entropía: difícil de atrapar, no pesa, y cumple la Segunda Ley de la Termodinámica, es decir, tiende a incrementarse"<br /><em> -- Norman Augustine</em><br /><br />6. "El software es un gas: se expande hasta llenar su contenedor"<br /><em> -- Nathan Myhrvold</em><br /><br />7. "Todas las piezas deben unirse sin ser forzadas. Debe recordar que los componentes que está reensamblando fueron desmontados por usted, por lo que si no puede unirlos debe existir una razón. Pero sobre todo, no use un martillo"<br /><em> -- Manual de mantenimiento de IBM, año 1925</em><br /><br />8. "Los estándares son siempre obsoletos. Eso es lo que los hace estándares"<br /><em> -- Alan Bennett</em><br /><br />9. "La física es el sistema operativo del Universo"<br /><em> -- Steven R Garman</em><br /><br />10. "El hardware es lo que hace a una máquina rápida; el software es lo que hace que una máquina rápida se vuelva lenta"<br /><em> -- Craig Bruce</em><br /><br /><h3>Conocimiento</h3>11. "La imaginación es más importante que el conocimiento. El conocimiento es limitado, mientras que la imaginación no"<br /><em> -- Albert Einstein</em><br /><br />12. "El mayor enemigo del conocimiento no es la ignorancia, sino la ilusión del conocimiento"<br /><em> -- Stephen Hawking</em><br /><br />13. "Cuanto más sabes, más te das cuenta de que no sabes nada"<br /><em> -- Sócrates</em><br /><br />14. "Dime y lo olvido, enséñame y lo recuerdo, involúcrame y lo aprendo"<br /><em> -- Benjamín Franklin</em><br /><br />15. "El auténtico conocimiento es conocer la extensión de la propia ignorancia"<br /><em> -- Confucio</em><br /><br />16. "Si la gente no hiciera cosas estúpidas, nunca se podría haber hecho nada inteligente"<br /><em> -- Ludwig Wittgenstein</em><br /><br />17. "Obtener información de internet es como intentar beber agua de una boca de incendios"<br /><em> -- Mitchell Kapor</em><br /><br /><h3>Usuarios</h3>18. "Si piensas que los usuarios de tus programas son idiotas, sólo los idiotas usarán tus programas" <br /><em> -- Linus Torvalds</em><br /><br />19. "Desde el punto de vista de un programador, el usuario no es más que un periférico que teclea cuando se le envía una petición de lectura"<br /><em> -- P. Williams</em><br /><br />20. "¿Dónde está la tecla 'ANY'?"<br /><em> -- Homer Simpson, frente a un mensaje "press any key"</em><br /><br />21. "Los ordenadores son buenos siguiendo instrucciones, no leyendo tu mente"<br /><em> -- Donald Knuth</em><br /><br />22. "Sólo hay un problema con el sentido común: que no es demasiado común"<br /><em> -- Milt Bryce</em><br /><br />23. "Tus clientes más descontentos son tu mayor fuente de aprendizaje"<br /><em> -- Bill Gates</em><br /><br />24. "Tenemos que cambiar la tradicional actitud ante la construcción de software. En vez de pensar que nuestra principal tarea es indicar a un ordenador qué hacer, concentrémonos en explicar a las personas lo que queremos que el ordenador haga"<br /><em> -- Donald E. Knuth</em><br /><br /><h3>Internet</h3>25. "¿Internet? No estamos interesados en eso"<br /><em> -- Bill Gates</em><br /><br />26. "La mejor forma de obtener información correcta de los foros de Usenet es enviar algo incorrecto y esperar las correcciones"<br /><em> -- Matthew Austern</em><br /><br /><h3>Profesionales</h3>27. "La mayoría de expertos está de acuerdo en que la causa más probable de destrucción del mundo sería por accidente; y aquí es donde entramos nosotros: somos profesionales de la informática, causamos accidentes"<br /><em> -- Nathaniel Borenstein</em><br /><br />28. "Dicen que los pesimistas ven el vaso medio vacío; los optimistas, en cambio, lo ven medio lleno. Los ingenieros, por supuesto, ven que el vaso es el doble de grande de lo que sería necesario"<br /><em> -- Bob Lewis</em><br /><br />29. "Si en una sala llena de diseñadores de software dos de ellos están de acuerdo, eso es una mayoría"<br /><em> -- Bill Curtis</em><br /><br />30. "Es importante destacar que ningún ingeniero software con ética consentiría escribir un procedimiento llamado DestruirBaghdad. Su ética le obligaría a escribir un procedimiento DestruirCiudad, al que se pasaría el parámetro Baghdad"<br /><em> -- Nathaniel S. Borenstein</em><br /><br />31. "Una de las cosas más fascinantes de los programadores es que no puedes saber si están trabajando o no sólo con mirarlos. A menudo están sentados aparentemente tomando café, chismorreando o mirando a las nubes. Sin embargo, es posible que estén poniendo en orden todas las ideas individuales y sin relación que pululan por su mente"<br /><em> -- Charles M. Strauss</em><br /><br />32. "Si piensas que vales lo que sabes, estás muy equivocado. Tus conocimientos de hoy no tienen mucho valor más allá de un par de años. Lo que vales es lo que puedes llegar a aprender, la facilidad con la que te adaptas a los cambios que esta profesión nos regala tan frecuentemente"<br /><em> -- José M. Aguilar, en <a style='text-decoration: none;' href="http://www.variablenotfound.com/2007/09/10-motivos-por-los-que-tu-blog-te-ayuda.html" title="Tu blog te ayuda a encontrar empleo">cómo tu blog te ayuda a encontrar empleo</a></em><br /><br /><h3>Programación</h3>33. "Los programas deben ser escritos para que los lean las personas, y sólo incidentalmente, para que lo ejecuten las máquinas"<br /><em> -- Abelson and Sussman</em><br /><br />34. "Comentar el código es como limpiar el cuarto de baño; nadie quiere hacerlo, pero el resultado es siempre una experiencia más agradable para uno mismo y sus invitados"<br /><em> -- Ryan Campbell</em><br /><br />35. "Tenemos que dejar de optimizar para programadores y comenzar a optimizar para usuarios"<br /><em> -- Jeff Atwood</em><br /><br />36. "La programación en bajo nivel es buena para el alma del programador"<br /><em> -- John Carmack</em><br /><br />37. "Está bien investigar y resolver misteriosos asesinatos, pero no deberías necesitar hacerlo con el código. Simplemente deberías poder leerlo"<br /><em> -- Steve McConnell</em><br /><br />38. "Si queremos contar líneas de código, no deberíamos referirnos a ellas como líneas producidas, sino como líneas consumidas"<br /><em> -- Edsger Dijkstra</em><br /><br />39. "La programación puede ser divertida, al igual que la criptografía; sin embargo, ambas no deberían combinarse"<br /><em> -- Kreitzberg and Shneiderman</em><br /><br />40. "Antes de que un software sea reutilizable debería ser utilizable"<br /><em> -- Ralph Johnson</em><br /><br />41. "Si automatizas un procedimiento desastroso, obtienes un procedimiento desastroso automatizado"<br /><em> -- Rod Michael</em><br /><br />42. "Ley de Alzheimer de la programación: si lees un código que escribiste hace más de dos semanas es como si lo vieras por primera vez"<br /><em> -- Via Dan Hurvitz</em><br /><br />43. "Es más fácil cambiar las especificaciones para que encajen con el software que hacerlo al revés"<br /><em> -- Alan Perlis</em><br /><br />44. "Menos del 10% del código tienen que ver directamente con el propósito del sistema; el resto tiene que ver con la entrada y salida, validación de datos, mantenimiento de estructuras de datos y otras labores domésticas"<br /><em> -- Mary Shaw</em><br /><br />45. "Si tienes una función o procedimiento con diez parámetros, probablemente hayas olvidado uno"<br /><em> -- Alan Perlis</em><br /><br />46. "Es raro que mantener el código de otro desarrollador sea como entrar en un edificio de gran diseño que admiras mientras paseas por él y planeas cómo añadirle un ala o algún elemento decorativo. Lo más frecuente es que sea como tirarse de cabeza a un gran montón de basura maloliente"<br /><em> -- Bill Venners</em><br /><br />47. "La generación de código, como beber alcohol, es bueno si se hace con moderación"<br /><em> -- Alex Lowe</em><br /><br /><h3>Desarrollo</h3>48. "La simplicidad llevada al extremo se convierte en elegancia"<br /><em> -- Jon Franklin</em><br /><br />49. "Un programa nunca está completo por debajo del 90% ni por encima del 95%"<br /><em> -- Terry Baker</em><br /><br />50. "Cuando estás en un atasco de tráfico con un Porsche, todo lo que puedes hacer es consumir más combustible que el resto estando parado. La escalabilidad va de construir carreteras más anchas, no coches más rápidos"<br /><em> -- Steve Swartz</em><br /><br />51. "Todo el mundo sabe el peligro de la optimización prematura. Pienso que deberíamos estar igualmente preocupados con el diseño prematuro, es decir, el hecho de diseñar demasiado pronto lo que un programa debería hacer"<br /><em> -- Paul Graham</em><br /><br />52. "Programar sin una arquitectura o diseño en mente es como explorar una gruta sólo con una linterna: no sabes dónde estás, dónde has estado ni hacia dónde vas"<br /><em> -- Danny Thorpe</em><br /><br />53. "La mejor forma de predecir el futuro es implementarlo"<br /><em> -- David Heinemeier Hansson</em><br /><br />54. "Lo realmente necesario es saberlo todo sobre los cambios en la información. Nadie quiere o necesita que le recuerden 16 horas al día que tiene sus zapatos puestos"<br /><em> -- David Hubel</em><br /><br />55. "En dos ocasiones me han preguntado: 'si pone datos incorrectos en la máquina, ¿saldrán las respuestas correctas?'. Soy absolutamente incapaz de hacerme una idea del tipo de confusión de ideas que pueden provocar que alguien haga una pregunta así"<br /><em> -- Charles Babbage</em><br /><br />56. "Hazlo todo tan simple como sea posible, pero no más simple"<br /><em> -- Albert Einstein</em><br /><br />57. "Hoy en día la mayoría del software existe no para resolver un problema, sino para actuar de interfaz con otro software"<br /><em> -- I. O. Angell</em><br /><br />58. "Unas buenas especificaciones incrementará la productividad del programador mucho más de lo que puede hacerlo cualquier herramienta o técnica"<br /><em> -- Milt Bryce</em><br /><br />59. "La diferencia entre la teoría y la práctica es que, en teoría, no hay diferencia entre la teoría y la práctica"<br /><em> -- Richard Moore, desarrollador de KDE</em><br /><br /><h3>Errores y depuración</h3>60. "No documentes el problema; arréglalo"<br /><em> -- Atli Björgvin Oddsson</em><br /><br />61. "Por norma, los sistemas software no funcionan bien hasta que han sido utilizados y han fallado repetidamente en entornos reales"<br /><em> -- Dave Parnas</em><br /><br />62. "Si el código y los comentarios no coinciden, posiblemente ambos sean erróneos"<br /><em> -- Norm Schryer</em><br /><br />63. "Creo que es una nueva característica. No le cuentes a nadie que fue un accidente"<br /><em> -- Larry Wall</em><br /><br />64. "Si no las capturas y procesas, cerramos tu aplicación. Esto incrementa enormemente la fiabilidad de tu sistema"<br /><em> -- Anders Hejlsberg, sobre las excepciones en .Net</em><br /><br />65. "Cuando se está depurando, el programador novato introduce código correctivo; el experto elimina el código defectuoso"<br /><em> -- Richard Pattis</em><br /><br />66. "En un proyecto software con diez personas, probablemente tres de ellas introducen tantos errores que podríamos considerar su productividad como negativa"<br /><em> -- Gordon Schulmeyer</em><br /><br />67. "Es inevitable que la gente programe mal, y la formación no mejorará sustancialmente las cosas. Tenemos que aprender a vivir con ello"<br /><em> -- Alan Perlis</em><br /><br />68. "El testing de componentes puede ser muy efectivo para mostrar la presencia de errores, pero absolutamente inadecuado para demostrar su ausencia"<br /><em> -- Edsger Dijkstra</em><br /><br /><h3>Lenguajes y tecnologías</h3>69. "La gestión manual de bloques de memoria en C es como hacer malabarismos con pastillas de jabón en la ducha de la prisión: todo diversión hasta que cometes un fallo"<br /><em> -- Un usuario anónimo de un foro Usenet</em><br /><br />70. "No pueden existir concursos de Perl ofuscado; no tendría sentido"<br /><em> -- Jeff Polk (Nota: ¡sí que los hay!)</em><br /><br />71. "Java es lo más penoso que le ha ocurrido a la informática desde MS-DOS"<br /><em> -- Alan Kay</em><br /><br />72. "Sólo hay dos cosas malas en C++: el concepto inicial y la implementación"<br /><em> -- Bertrand Meyer</em><br /><br />73. "Era una broma, ¿vale? Si hubiéramos pensado que iba a usarse no la habríamos escrito"<br /><em> -- Mark Andreesen, hablando de la etiqueta BLINK de HTML</em><br /><br />74. "Los Servicios Web son como el sexo entre los adolescentes. Todos hablan de hacerlo, pero aquellos que realmente lo hacen, lo hacen muy mal"<br /><em> -- Michelle Bustamante</em><br /><br />75. "Perl: el único lenguaje cuyo código es prácticamente igual antes y después de someterlo a una encriptación RSA"<br /><em> -- Keith Bostic</em><br /><br />76. "No trabajé duro para hacer Ruby perfecto para todo el mundo, porque todos somos diferentes. Intenté hacer Ruby perfecto para mí, así que puede que a tí no te lo parezca; probablemente, el mejor lenguaje para Guido van Rossum es Python"<br /><em> -- Yukihiro Matsumoto, aka "Matz", creador de Ruby</em><br /><br />77. "XML no es más lenguaje de programación que unas notas sobre una servilleta de papel"<br /><em> -- Charles Simonyi</em><br /><br />78. "BASIC es a la programación lo que QWERTY a la mecanografía"<br /><em> -- Seymour Papert</em><br /><br />79. "Se ha descubierto que C++ dispone de una gran facilidad para ocultar los detalles triviales de un programa... así como dónde están sus bugs"<br /><em> -- David Keppel</em><br /><br />80. "UNIX es simple. Sólo necesita un genio para entender su simplicidad"<br /><em> -- Dennis Ritchie</em><br /><br />81. "Algunos desarrolladores cuando se enfrentan a un problema piensan que la solución es usar expresiones regulares. En este momento, ya tienen dos problemas"<br /><em> -- Jamie Zawinski</em><br /><br /><h3>Seguridad</h3>82. "Pienso que los virus informáticos muestran la naturaleza humana: la única forma de vida que hemos creado hasta el momento es puramente destructiva"<br /><em> -- Stephen Hawking</em><br /><br />83. "El único sistema seguro es aquél que está apagado en el interior de un bloque de hormigón protegido en una habitación sellada rodeada por guardias armados"<br /><em> -- Gene Spafford</em><br /><br />84. "Saber romper medidas de seguridad no hacen que seas hacker, al igual que saber hacer un puente en un coche no te convierte en un ingeniero de automoción"<br /><em> -- Eric Raymond</em><br /><br />85. "Las organizaciones gastan millones de dólares en firewalls y dispositivos de seguridad, pero tiran el dinero porque ninguna de estas medidas cubre el eslabón más débil de la cadena de seguridad: la gente que usa y administra los ordenadores"<br /><em> -- Kevin Mitnick</em><br /><br />86. "Si piensas que la tecnología puede solucionar tus problemas de seguridad, está claro que ni entiendes los problemas ni entiendes la tecnología"<br /><em> -- Bruce Schneier</em><br /><br />87. "Los bulos (hoaxes) que circulan por internet usan la debilidad del ser humano para asegurar su replicación y distribución. En otras palabras, utilizan los resquicios del Sistema Operativo Humano"<br /><em> -- Stewart Kirkpatrick</em><br /><br />88. "Las contraseñas son como la ropa interor. No puedes dejar que nadie la vea, debes cambiarla regularmente y no debes compartirla con extraños"<br /><em> -- Chris Pirillo</em><br /><br /><h3>Empresa</h3>89. "En realidad no trato de destruir a Microsoft: eso será sólo un efecto colateral no intencionado"<br /><em> -- Linus Torvalds</em><br /><br />90. "Sí, tenemos unas reglas de vestuario en la empresa. Tienes que vestirte"<br /><em> -- Scott McNealy, co-fundador de Sun Microsystems</em><br /><br />91. "En el mundo del software, los activos más importantes de la compañía se van a casa todas las noches. Si no se les trata bien, pueden no volver al día siguiente"<br /><em> -- Peter Chang</em><br /><br />92. "Es mejor esperar a que un desarrollador productivo esté disponible que esperar a que el primer desarrollador disponible sea productivo"<br /><em> -- Steve C McConnell</em><br /><br />93. "No soy de los que piensan que Bill Gates es el diablo. Simplemente sospecho que si Microsoft alguna vez se encontrara con el diablo, no necesitarían un intérprete"<br /><em> -- Nicholas Petreley</em><br /><br /><h3>Predicciones</h3>94. “En dos años el problema del spam se habrá resuelto”<br /><em> -- Bill Gates, 2004</em><br /><br />95. "El problema de los virus es pasajero. En un par de años estará resuelto"<br /><em> -- John McAfee, 1988</em><br /><br />96. “Los virus informáticos son una leyenda urbana”<br /><em> -- Peter Norton, 1988</em><br /><br />97. "En 2031, los abogados serán componentes habituales de la mayoría de los equipos de desarrollo"<br /><em> -- Grady Booch</em><br /><br />98. “No sé cómo será el lenguaje del año 2000, pero sé que se llamará Fortran”<br /><em> -- C. A. Hoare, 1982</em><br /><br />99. "En el futuro es posible que los ordenadores no pesen más de 1,5 toneladas"<br /><em> -- Popular mechanics, 1949</em><br /><br />100. “Veo poco potencial comercial en Internet, al menos durante diez años”<br /><em> -- Bill Gates, 1994</em><br /><br />101. "Antes de que el hombre alcance la luna, el correo será enviado en unas horas desde Nueva York a California, Inglaterra, India o Australia con misiles guiados. Estamos en la era del misil-correo"<br /><em> -- Arthur Summerfield, 1959, Correos de los Estados Unidos</em><br /><br /><br /><hr /><br /><strong>Más citas en:</strong> <a title="Primer post de la serie" href="http://www.variablenotfound.com/2008/02/101-citas-clebres-del-mundo-de-la.html">101 citas célebres del mundo de la informática</a><br />Publicado originalmente en: <a href="http://www.variablenotfound.com/">http://www.variablenotfound.com</a>.José M. Aguilarhttp://www.blogger.com/profile/11683750380776435448noreply@blogger.comtag:blogger.com,1999:blog-27752126.post-91982560388036173012008-04-02T21:30:00.001+02:002008-04-02T21:29:47.865+02:00Clases parciales en C# y VB.NET<a href="http://bp3.blogger.com/_O9D62hXq-ng/R-KyJyDcDSI/AAAAAAAAATw/Gfz7uODxBoI/s1600-h/puzzle.png"><img id="BLOGGER_PHOTO_ID_5179898402554252578" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; CURSOR: hand" alt="Piezas de puzzle" src="http://bp3.blogger.com/_O9D62hXq-ng/R-KyJyDcDSI/AAAAAAAAATw/Gfz7uODxBoI/s200/puzzle.png" border="0" /></a>Aunque 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.<br /><br />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.<br /><br />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 <code>partial</code> en la definición del tipo:<br /><pre><code> <span style="color:green;">' VB.NET </span><br /> Public Partial Class Persona<br /> ...<br /> End Class<br /><br /> <span style="color:green;">// C#</span><br /> public partial class Persona<br /> {<br /> ...<br /> }<br /></code></pre> <br />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#.<br /><br />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 <code>partial</code> siempre que la clase lo sea, e independientemente del lenguaje utilizado, pues contribuirá a la mantenibilidad del código.<br /><br />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.<br /><br />Para comprobarlo he creado un pequeño código con dos clases exactamente iguales, salvo en su nombre. Una de ellas se denomina <code>PersonaTotal</code>, y está definida como siempre, en un único archivo; la otra, <code>PersonaParcial</code>, es parcial y la he troceado en tres archivos, como sigue:<br /><pre><code> <span style="color:green;">// <strong>*** Archivo PersonaParcial.Propiedades.cs ***</strong><br /> // Aquí definiremos todas las propiedades</span><br /> partial class PersonaParcial<br /> {<br /> public string Nombre { get; set; }<br /> public string Apellidos { get; set; }<br /> }<br /> <br /> <span style="color:green;">// <strong>*** Archivo PersonaParcial.IEnumerable.cs ***</strong><br /> // Aquí implementaremos el interfaz IEnumerable</span><br /> partial class PersonaParcial: IEnumerable<br /> {<br /> public IEnumerator GetEnumerator()<br /> {<br /> throw new NotImplementedException();<br /> }<br /> }<br /><br /> <span style="color:green;">// <strong>*** Archivo PersonaParcial.Metodos.cs ***</strong><br /> // Aquí implementaremos los métodos que necesitemos</span><br /> partial class PersonaParcial<br /> {<br /> public override string ToString()<br /> {<br /> return Nombre + " " + Apellidos;<br /> }<br /> }<br /></pre></code> <br />Y efectivamente, el resultado de compilar ambas clases, según se puede observar con ILDASM es idéntico:<br /><br /><a href="http://bp2.blogger.com/_O9D62hXq-ng/R-KqxiDcDRI/AAAAAAAAATo/WWMfV3zLsgQ/s1600-h/clasesparciales.png"><img id="BLOGGER_PHOTO_ID_5179890289361030418" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="Clases generadas tras la compilación" src="http://bp2.blogger.com/_O9D62hXq-ng/R-KqxiDcDRI/AAAAAAAAATo/WWMfV3zLsgQ/s400/clasesparciales.png" border="0" /></a><br /><br />A la hora de crear clases parciales es conveniente tener los siguientes aspectos en cuenta:<ul><li>Los atributos de la clase resultante serán la combinación de los atributos definidos en cada una de las partes.</li><li>El tipo base de los distintos fragmentos debe ser el mismo, o aparecer sólo en una de las declaraciones parciales.</li><li>Si se trata de una <a href="http://www.variablenotfound.com/2007/03/generics-en-c.html">clase genérica</a>, los parámetros deben coincidir en todas las partes.</li><li>Los interfaces que implemente la clase resultante será la unión de todos los implementados por las distintas secciones.</li><li>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.</li></ul><br />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.<br /><br />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 <a href="http://es.wikipedia.org/wiki/Microsoft_Visual_SourceSafe">Sourcesafe</a> o <a href="http://es.wikipedia.org/wiki/Subversion">Subversion</a>), 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.<br /><br />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: <ul><li>por ejemplo, separar la interfaz (los miembros visibles desde el exterior de la clase) y por otra los miembros privados a la misma</li><li>o bien separar las porciones que implementan interfaces, o sobreescriben miembros de clases antecesoras de los pertenecientes a la propia clase</li><li>o separar temas concernientes a distintos dominios o aspectos</li></ul>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.<br /><br />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.<br /><br />Publicado en: <a href="http://www.variablenotfound.com/">http://www.variablenotfound.com/</a>.José M. Aguilarhttp://www.blogger.com/profile/11683750380776435448noreply@blogger.comtag:blogger.com,1999:blog-27752126.post-28075108605243860902008-03-30T22:00:00.003+02:002008-03-30T22:01:51.298+02:00Crea un traductor en tu web con Google AJAX Language API<a href="http://bp3.blogger.com/_O9D62hXq-ng/R-_wliDcDYI/AAAAAAAAAUg/jOGVOFg-JEk/s1600-h/mundo.png"><img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_O9D62hXq-ng/R-_wliDcDYI/AAAAAAAAAUg/jOGVOFg-JEk/s200/mundo.png" border="0" alt="Mundo" id="BLOGGER_PHOTO_ID_5183626223713783170" /></a>Vía <a title="Visitar google.dirson.com" href="http://google.dirson.com/post/3920-api-traduccion-idiomas-ajax/">Dirson </a> 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.<br /><br />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.<br /><br /><h3>Paso 1: Incluir el API</h3>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:<br /><pre><code> <script type="text/javascript"<br /> src="http://www.google.com/jsapi"><br /> </script><br /> <script type="text/javascript"> <br /> google.load("language", "1"); <br /> </script><br /></code></pre> <br />El primer bloque descarga a cliente la librería javascript <a href="http://code.google.com/apis/ajax/documentation/" title="Información sobre Ajax API loader">Ajax API Loader</a>, 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 <code>google.load</code> el API "language" (traductor), en su versión 1 (el segundo parámetro).<br /><br /><h3>Paso 2: Creamos el interfaz</h3>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. <br /><br />El resultado será como el mostrado en la siguiente imagen.<br /><br /><a href="http://bp0.blogger.com/_O9D62hXq-ng/R--A3yDcDXI/AAAAAAAAAUY/T8MzVW5Z8as/s1600-h/traductor.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_O9D62hXq-ng/R--A3yDcDXI/AAAAAAAAAUY/T8MzVW5Z8as/s400/traductor.png" border="0" alt="Interfaz del traductor" id="BLOGGER_PHOTO_ID_5183503391944084850" /></a><br /><br />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 <code>TextArea</code> 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 <code>onclick</code>. Por último, se reservará una zona en la que insertaremos el resultado de la traducción.<br /><br />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.<br /><br /><h3>Paso 3: ¡Traducimos!</h3>La pulsación del botón provocará la llamada a la función <code>Onclick()</code>, desde donde se realizará la traducción del texto introducido en el TextArea.<br /><br />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.<br /><pre><code> var text = document.getElementById("text").value;<br /> var srcLang = document.getElementById("srcLang").value;<br /> var dstLang = document.getElementById("dstLang").value;<br /></code></pre> <br />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:<br /><pre><code><br /> google.language.translate(text, srcLang, dstLang, function(result) <br /> {<br /> if (!result.error) <br /> { <br /> var resultado = document.getElementById("result"); <br /> resultado.innerHTML = result.translation; <br /> }<br /> else alert(result.error.message);<br /> }<br /> );<br /></code></pre> <br />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 href="http://www.variablenotfound.com/2007/11/aadir-funciones-con-parmetros-al-evento.html" title="Funciones con parámetros en OnLoad()">añadir funciones con parámetros al evento OnLoad</a>).<br /><br />Para los queráis jugar con esto directamente, ahí va el código listo para un copiar y pegar.<br /><br /><pre><code><html><br /><head><br /> <title>Traductor</title><br /> <script type="text/javascript" src="http://www.google.com/jsapi"></script><br /> <script type="text/javascript"> <br /> google.load("language", "1"); <br /> </script><br /></head><br /><br /><body><br /> <textarea id="text" rows="8" cols="40">Hi, how are you?</textarea><br /> <br /><br /> <button onclick="onClick()"><br /> Traducir</button><br /> Del idioma<br /> <select id="srcLang"><br /> <option value="">(Auto)</option><br /> <option value="es">Español</option><br /> <option value="en">In