Mostrando entradas con la etiqueta Vitácora Técnica. Mostrar todas las entradas
Mostrando entradas con la etiqueta Vitácora Técnica. Mostrar todas las entradas

martes, 19 de junio de 2012

SURFACE: LA NUEVA TABLETA DE MICROSOFT

Microsoft recientemente ha presentado su propia línea de tablets potenciadas con los próximos sistemas operativos Windows RT y 8 Pro: Microsoft Surface –marcada con el nuevo logo.

Dos especificaciones grandes de hardware estarán disponibles, las cuales dependerán de la versión de SO que venga con la tableta, pero el producto se ve enteramente prometedor.

Una corriente de comentarios y opiniones ha inundado la Internet desde entonces, pero personalmente, creo que es un producto muy interesante. Todo lo que espero es que esta vez, MSFT le brinde a esta línea de productos un destino muy diferente al que tuvieron los dispositivos Zune (HD) …

Basta de palabras, así que aquí está el video con la presentación:

Algo me dice que la era del ratón (y del teclado, como hoy lo conocemos), está llegando a su final.

Que lo disfruten!
~Pete

> Vínculo a la versión en inglés.

sábado, 15 de octubre de 2011

CAMBIAR EL TAMAÑO DE UN DISCO EXISTENTE EN VIRTUALBOX

[ Si piensas que este artículo es útil, por favor considera descargar mi juego "Just Survive XP" en el Appstore (gratis por tiempo limitado):

=> http://itunes.apple.com/app/just-survive-xp/id461876025 ]

Ayer estaba instalando una nueva máquina virtual en mi equipo de escritorio con VirtualBox y, no me pregunten porqué puesto que no lo se, pero cometí un error de principiante …

… cuando creé el archivo vdi para el disco duro virtual, olvidé asignar un tamaño personalizado por lo cual terminó con los 20GB que vienen por defecto. Ishhh!

Pronto noté mi error cuando me percaté que precisaría más espacio en dicho disco para instalar todo el conjunto de herramientas que quería usar, así que abrí VirtualBox a efectos de cambiar el tamaño de disco y adivinen qué … no encontré forma en la configuración de hacerlo directamente. Que macana …

Talvez estaba ahí en algún lugar pero no lo encontré así que temiendo lo pero –esto es, reinstalar todo en una nueva imagen desde el principio, busqué en Internet por una solución. Por qué no leer el archivo de ayuda de VirtualBox, cierto?

Bueno, para mi sorpresa encontré todo tipo de soluciones engorrosas como clonar el archivo .vdi y usar un gestor de particiones para blá, blá, blá. Cansado de estos callejones sin salida, finalmente hice lo que debí hacer desde un principio: leí el archivo de ayuda!

Sí; la solución estuvo siempre ahí: VirtualBox incluye una herramienta de línea de comando llamada “VBoxManage” con un conjunto interesante de opciones disponibles y una de ellas es “modifyhd”. Su sintaxis es la siguiente:

VBoxManage modifyhd <uuid><filename>
[—type normal | writethrough | immutable | shareable
readonlymultiattach]
[--autoreset onoff]
[—compact]
[--resize <megabytes>--resizebyte <bytes>]

Una vez abierto la terminal de línea de comandos en el Sistema Operativo anfitrión de nuestro equipo, una manera simple de usar este comando sería, digamos:

VBoxManage modifyhd
“<ruta_completa>/<nombre_de_archivo>”
--resize 30720

Es importante notar que no deberían usar una ruta relativa sino una ruta completa al archivo .vdi par resultados óptimos. Así que, si el archivo .vdi se ubica en “C:/mi_ruta/mi_archivo_vdi.vdi”, usen esta cadena de texto en el comando (en caso de Sistemas Operativos distintos a Windows, usen la ruta desde la raíz).

Si todo sale bien debería ver una nueva línea de texto mostrando el progreso:

0%…10%…20%…30%…40%…50%…60%…70%…80%…90%…100%

Pero a veces, como en mi caso, las cosas no resultan ser tan simples. Pueden encontrarse frente a un mensaje de error como:

ERROR: Cannot register the hard disk 'C:/mypath/myvdifile.vdi' with UUID {someUUIDtext} because a hard disk 'C:/mypath/myvdifile.vdi' with UUID {someUUIDtext} already exists in the media registry ('…/VirtualBox.xml')
Details: code NS_ERROR_INVALID_ARG (0x80070057), component VirtualBox, interface IVirtualBox, callee nsISupports
Context: "OpenHardDisk(Bstr(szFilenameAbs), AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), srcDisk.asOutParam())" at line XX of file VBoxManageDisk …

Si esto sucede no entren en pánico; una solución que funcionó para mí fue utilizar el UUID asignado al disco duro virtual, en vez de la ruta completa al archivo, por lo que si el UUID es “{13f2…-…-…342a}” entonces el comando debería lucir como:

VBoxManage modifyhd 13f2…-…-…342a
--resize 30720

Ahora bien, cuando el nuevo tamaño del archivo .vdi sea asignado, todo lo que deben hacer es abrir la máquina virtual, entrar al sistema operativo, y asignar el nuevo espacio libre al disco duro. Si el sistema huésped es Windows, entonces ejecuten la herramienta de gestión provista (pulsen botón derecho sobre el ícono del Equipo y selección “Administrar”), encuentren el disco virtual y extiendan su tamaño.

Bueno, eso es todo. Espero que encuentren estos consejos útiles.

Salud!
~Pete

> Vínculo a la versión en inglés.

jueves, 14 de octubre de 2010

REEMPLAZANDO EL API DE GESTION DE CONTENIDO: PRELUDIO

Como mencioné brevemente en una publicación anterior, por un período de tiempo (ya cercano a 2 años) he estado desarrollando mi propio API de gestión de contenido como reemplazo del liberado como parte del Framework de XNA.

¿Por qué? Al comienzo estaba tratando de sortear las limitaciones de este último respecto a su incapacidad de gestionar con propiedad la operación Dispose. Y al hacerlo, noté -cuando miré el código fuente del original con “Reflector”- que había espacio para mejoras (con un rediseño).

Luego de casi dos años de desarrollarlo como parte de mi propio motor de videojuegos basado en XNA (estando este último aún en desarrollo), decidí rediseñar mi gestor de contenido como un módulo independiente para que pueda ser utilizado con XNA sin tener que enchufar mi motor (ni completamente ni en parte).

No voy a liberar el código fuente de mi API, así que en las próximas entregas de esta serie de artículos voy a discutir públicamente lo que mi API tiene para ofrecer, de forma que cualquiera pueda leerlo, enfocándome en “El Qué” y no en “El Cómo”; así que estaré presentando principalmente características y resultados (si … ya se, ya se, … “que plomazo!”).

Entonces, qué sigue? En la parte 1 (y muy posiblemente además en la parte 2) voy a presentar mi API, explicando las diferencias con la que se encuentra por defecto, y luego de ello empezaré a hablar de los resultados obtenidos (sí! con cifras, capturas de pantalla y demás) para las diferentes plataformas donde corrí las pruebas.

Hasta la próxima,
~Pete

> Vínculo a la versión en inglés.

viernes, 10 de septiembre de 2010

OBTENIENDO UNA REFERENCIA AL OBJETO QUE INVOCA A UN METODO EN C#

Ok, luego de tomarme un mes de asueto de cualquier actividad de blogueo, supongo que es tiempo de ponerme al día y publicar de nuevo. Y creo que no hay mejor forma de hacerlo que escribir un artículo explicando una forma de obtener una referencia a un objeto que llama a un método específico.

Durante los últimos dos años he estado trabajando duro en un API de reemplazo al Content Manager del Framework de XNA para usarla en mi propio motor de videojuegos “que nunca finaliza” (voy a escribir sobre esto más adelante). En etapas tempranas de su desarrollo encontré una forma de solucionar esta traba referencial que ha estado creando dolores de cabeza a muchos desarrolladores, globalmente (siendo yo, uno de ellos, por mucho tiempo).

Aunque debo admitir que en algún punto del ciclo de desarrollo de mi API decidí abandonar esta solución “parche”, luego de leer uno de los artículos mencionados por Shawn aquí (adivinen cuál), entiendo que es una gran oportunidad para hablar de ello.

Un poco de historia … previo a encontrar esta solución lo intenté todo: desde probar usar, sin suerte, la información contenida en instancias del StackFrame e incluso delegados hasta finalmente “desistir” y utilizar un enfoque ingenuo como ser el pasar al invocador como parámetro en la firma de la operación. Pero, luego vino el Framework .NET 3 y los métodos de extensión al rescate!

Por favor coincidan conmigo que el ejemplo presentado en este artículo es uno simple para mostrar su uso, pero créanme cuando digo que puede extenderse a escenarios complejos.

Dicho eso … si desean saber cuál objeto está invocando a un cierto método de una clase como (que puede esta sellada o nó):

  public sealed class MyCalledClass
  {
    internal bool JustReturnTrue(int myParameter)
    {
      return true;
    }
  }

Siendo el invocador, digamos, una especificación de la siguiente clase:

  public abstract class MyCallingClass
  {
    public void Print()
    {
      Console.WriteLine("I am the object which is just about to call
         the monitored operation named 'JustReturnTrue' …"
);
    }
  }

Entonces todo lo que tienen que hacer es implementar el método de extensión que sigue para los tipos que deseen (en mi ejemplo, “MyCallingClass”):

  public static class MyExtensionMethod
  {
    public static bool ExecuteOperation<T>
      (this T mycallingClass, int myParameter)
      where T: MyCallingClass
    {
      mycallingClass.Print(); // Cámbienlo con sus propias cosas.
 
      return new MyCalledClass().JustReturnTrue(myParameter);
    }
  }

El truco aquí es diseñar y separar ensamblados y espacios de nombre de forma tal que todas las instancias de “MyCallingClass” no puedan ejecutar directamente la operación “JustReturnTrue” (dejo esa tarea como un ejercicio para el lector).

Pero hay un tema a seguir de cerca, sin embargo. Al hacer esto están de hecho agregando una llamada más (cosa que ya saben), lo cual no es en general un problema en Windows, pero para la XBox 360 y todos los dispositivos que utilicen el Compact Framework, podría resultar caro si se utiliza muchas veces en tarea o bucles intensivos o pesados.

Pero si Uds. realmente lo necesitan cuando la velocidad no es un inconveniente o para operaciones donde, por ejemplo, precisen establecer dueños de algo “automágicamente” tras bambalinas y luego evaluar si un dueño llama a una operación restringida antes de ejecutarla, entonces ahí lo tienen!

Sólo úsenlo sabiamente …
~Pete

> Vínculo a la versión en inglés.

jueves, 22 de octubre de 2009

DOS NUEVOS EJEMPLOS EN XNA CCO

Ya deben estar al tanto de estas novedades, pero por las dudas les comento que dos grandiosos nuevos ejemplos están disponibles en el sitio web de XNA Creators Club:

Ahora bien, ¿qué hay de grandioso en ellos? Me alegro que hayan preguntado.

(I) Muestra de Extensión de Modelos Con Piel

El primer ejemplo muestra cómo extender el proyecto original de modelos con piel ("skinned models”) de forma que:

  • Puedan “mover” una parte del modelo con piel de manera independiente a la secuencia de la animación (como en este caso, un brazo y la cabeza),
  • Puedan posicionar objetos en relación a un hueso específico (en este caso, un bate), y
  • Puedan anexar una geometría de cobertura al modelo animado (en este caso, esferas).

El código es una gran fuente de conocimiento técnico para programadores respecto a la técnica de “pelaje” pero, debido a unos pocos problemas en el modelo con piel en sí, ellos también pueden aprender cuan engorrosos y puntillozos pueden llegar a ser los procesos de (no sólo modelado y desenvoltura sino también) de inclusión de un esqueleto, pelaje y animación al modelo 3D, tal que se vea ok en todas y cada una de las situaciones (esperadas) antes de salir al mercado.

Primera captura de pantalla:

Como pueden ver aquí, cuando muevan la cabeza uno de los vértices permanece (casi) inmóvil, lo cual significa que o bien no fue asignado al hueso del cuello o, si lo fue, entonces sus “proporciones” deberían reajustarse.

Segunda foto:

Parece que la parte del cinto marcada arriba con anaranjado se mueve junto con la cadera mientras que el resto del cinto se mueve con el torso. Y por ende, la animación de caminar, incluso si se está comportando como se espera, parece algo rara.

Es posible que, s¡endo la parte de programación el único/principal propósito del ejemplo en sí, ello no sea relevante para programadores, pero para independientes (“indies”) como yo que tienden a desarrollar todas las partes del juego “por sí mismos”, es un ejemplo fabuloso de algúnos de los dolores de cabeza que van a encontrar cuando traten con el lado artístico del tema.

(II) Muestra de Primitivos 3D

El segundo presenta un conjuento de clases que necesitarán para crear cubos, esferas, cilindros, roscas y la famosa tetera!

Estos primitivos se encuentran usualmente en los más famosos aplicaciones de modelado, entre otras, como 3D Studio Max o Maya.

Se pueden preguntar: ¿por qué la tetera se la considera un primitivo? Bueno, gracias al tipo de operaciones poderasas que se utilizan para crearla de manera procedural: calculos de curvas bezier! Buenísimo, realmente …

Una sugerencia para el cilindro:

En el caso que algunos de Uds. quieran crear los triángulos que conforman cada tapa del cilindro tal que se distribuyan uniformemente en torno a un vértice central, en vez de hacia un lado (como se ve arriba), entonces tendrán que reemplazar el siguiente método en la clase “CylinderPrimitive”:
/// <summary>
/// Helper method creates a triangle fan to close the ends of the cylinder.
/// </summary>
void CreateCap( int tessellation, float height, float radius, Vector3 normal )
{
  // Create cap indices.
  for ( int i = 0; i < tessellation - 2; i++ )
  {
    if ( normal.Y > 0 )
    {
      AddIndex( CurrentVertex );
      AddIndex( CurrentVertex + ( i + 1 ) % tessellation );
      AddIndex( CurrentVertex + ( i + 2 ) % tessellation );
    }
    else
    {
      AddIndex( CurrentVertex );
      AddIndex( CurrentVertex + ( i + 2 ) % tessellation );
      AddIndex( CurrentVertex + ( i + 1 ) % tessellation );
    }
  }
 
  // Create cap vertices.
  for ( int i = 0; i < tessellation; i++ )
  {
    Vector3 position = GetCircleVector( i, tessellation ) * radius +
                       normal * height;
 
    AddVertex( position, normal );
  }
}

De forma que, en cambio, se parezca a:

/// <summary>
/// Helper method that creates a cap laying out triangles around a centered vertex.
/// </summary>
void CreateCap( int tessellation, float height, float radius, Vector3 normal )
{
  // Add a vertex in the center of the cap.
  AddVertex( normal * height, normal );
 
  // Create cap indices, taking into account the centered vertex (that is why
  // we use "CurrentVertex - 1" as the first element of the corresponding index).
  for ( int i = 0; i < tessellation; i++ )
  {
    if ( normal.Y > 0 )
    {
      AddIndex( CurrentVertex - 1 );
      AddIndex( CurrentVertex + ( i + 1 ) % tessellation );
      AddIndex( CurrentVertex + ( i + 2 ) % tessellation );
    }
    else
    {
      AddIndex( CurrentVertex - 1 );
      AddIndex( CurrentVertex + ( i + 2 ) % tessellation );
      AddIndex( CurrentVertex + ( i + 1 ) % tessellation );
    }
  }
 
  // Create cap vertices.
  for ( int i = 0; i < tessellation; i++ )
  {
    Vector3 position = GetCircleVector( i, tessellation ) * radius +
                       normal * height;
 
    AddVertex( position, normal );
  }
}

Siendo el resultado:

Esto no es para nada crítico, pero por cuestiones de (una mejor) desenvoltura -e incluso de crear más “secciones” (anillos) sobre cada tapa, vía programación- 2 vértices centrales y unos pocos triángulos adicionales son útiles sin dañar a la performance.

A escribir código!
~Pete

 

> Vínculo a la versión en inglés.

domingo, 26 de julio de 2009

COMO ARREGLAR PROBLEMAS LOCALES DE CONEXION DE TU XBOX 360 A LOS SERVIDORES DE XBOX LIVE

Unas semanas atrás, tuve que resetear mi router porque Windows Vista me estaba dando problemas con las conecciones cableadas LAN & WAN de mi PC de escritorio (nota: problema resuelto al pasar a Windows 7).

La cosa es que cuando reconfiguré mi router utilizando el archivo con la configuración grabada por última vez, mi XBox estaba teniendo problemas al intenar conectarse a los canales de XBox Live’s (tanto arcade como indie). De hecho, de vez en cuando solía recibir mensajes que decían que mi conexión se había perdido debido a errores como el 80072741 y demás (como luego verán en unos instantes, simplemente olvidé persistir a disco los valores de configuración adecuados para mi router. Uff!).

Cuando eso sucede, y suponiendo que el Equipo de XBox Live no está trabajando en los servidores (pruebas, actualizaciones, etc.), entonces algo errado debe estar sucediendo de lado suyo (como en mi caso).

Así que, qué podría estar mal?

  1. Tu router está roto,
  2. Tu router tiene un firmware defectuoso o corrupto,
  3. Estás conectando tu XBox 360 con un cable defectuoso,
  4. Tu 360 no puede obtener un dirección IP local del router,
  5. El cortafuegos (firewall) de tu router no permite que tu 360 se conecte a Internet,
  6. Tu router está realizando tareas de "Network Address Translation" (NAT) estrictas o moderadas,
  7. Obtienes algúnos mensajes extraños de error y/o pérdida de conexión al jugar algúnos videojuegos en línea en modo multijugador, y
  8. Otros problemas de conexión.

Si uno, unos pocos o algúnos de estos les pasó, entonces tal vez los siguientes consejos podrían ayudarles a resolver problemas de conexión. Lo cual significa? Que no hay garantía de solución.

POR LO QUE USEN ESTOS CONSEJOS BAJO SU PROPIO RIESGO!

Ahora que están avisados, continúen leyendo con atención …

Tu router está roto.

Compren uno nuevo en caso que no sea fácil o valga la pena repararlo. mientras tanto pueden intentar conectarse directamente a través del model (DSL).

Tu router tiene un firmware defectuoso o corrupto.

Vayan a la página de soporte del fabricante, descarguen el firmware más reciente para el modelo de router, y actualícenlo (primero, lean el manual del router para saber cómo proceder con la actualización).

Estás conectando tu XBox 360 con un cable defectuoso.

Sólo cambialo e intenta nuevamente.

Pero que si estoy utilizando una conexión inalámbrica? Entonces comprueben la configuración inalámbrica de su router, como, digamos el método de seguridad y la contraseña.

Tu XBox 360 no puede obtener un dirección IP local del router.

Primero verifiquen la configuración de red en la XBox 360: si desean obtenr una dirección IP estática o dinámica, los valores de dirección primario y secundario del “Sistema de Nombres de Dominio” (DNS), las direcciones IP de la puerta de enlace (gateway), etc.

Ahora bien, miren en el router el número máximo de conexiones permitidas al mismo tiempo. Talvéz las estén utilizando todas.

Si el router provée un servidio de “Protocolo de Configuración Dinámica de Alojamiento" (DHCP), entonces cualquier computer, consola y/o dispositivo LAN/WAN puede ser configurado para intentar obtener una dirección IP del router, dinámicamente.

Ello debería funcionar bien con sus consolas XBox 360 para los routers soportados, pero en caso que no sea así, sólo configuren la consola para obtener una dirección IP estática.

Cuál? Bueno, dicho de manera simple, una dirección IP que Uds. sepan que sus sistmeas normalmente no van a usar (por ejemplo, si tienen dos computadores y una consola y están permitiendo, digamos 8 conexiones, entonces establezcan la última como la dirección IP estática de la consola y probablemente les vaya bien).

A efectos de establecer una dirección IP estática, en la pizarra de la 360 naveguen hacia “System –> Network Settings –> Edit Settings”, y luego ingresen:

  • La dirección IP estática para la consola,
  • La máscara de subred (la misma que encontrarán en el router),
  • La dirección IP de la puerta de enlace (Gateway),
  • Las direcciones de los DNS primario y secundario.

Prueben la conexión de nuevo, y si todo sale bien, su 360 ahora debería tener acceso a la red local.

El cortafuegos (firewall) de tu router no permite que tu 360 se conecte a Internet.

Tener acceso a la red local no significa que el router les conceda acceso a Internet. A veces, el cortafuegos (firewall) del router frena cualquier intento de un dispositivo y/o de una serie de direcciones IP de alcanzar la Internet.

Si ese es el caso, entonces verifiquen las reglas de seguridad establecidas en el router. La mayoría de los routers les permite especificar cada IP para la cual quieran controlar su acceso a Internet, un rango de direcciones IP e incluso las direcciones de “Control de Acceso a Medios” (MAC) de sus dispoditivos.

Puesto que en caso, en mi caso, el número de dispositivos que se conectan a Internet es bajo, sólo especifico cada dirección MAC y listo!

también puede suceder que el acceso a Internet esté únicamente disponible durante ciertos días y horas por día. Por lo que también deberían ver esas reglas.

Tu router está realizando tareas de "Network Address Translation" (NAT) estrictas o moderadas.

Este es uno de los problemas más populares a cuando una console XBox 360 intenta establecer una conexión segura con los servidores de XBox Live.

En resúmen, no todos los puertos y protocolos necesarios para establecer una comunicación óptima están (debidamente) configurados.

Ok … ehh … qué?

Hagan lo siguiente: prueben sus conexiones de la 360 a la red local, a la Internet y finalmente a los servidores de Live, y si obtiene como resultado que dos de tres funcionan bien, siendo la última prueba la que “parcialmente” falla, entonces la función NAT de su router no es “Abierta”.

De hecho, si esa es la situación, pueden conectarse a Xbox Live pero dicha conexión no es óptima para casos donde quieran jugar, chatear, hablar e incluso aceptar invitaciones de amigos, en línea.

Para configurar los puertos y protocolos requeridos a fin de establecer una conexión "sana" a los servicios de XBox Live, deben o bien:

  • Ubicar su consola dentro de la "zona desmilitarizada” (DMZ), o
  • Configurar manualmente los valores específicos utilizando “Reenvío de puertos”.

Nota: para realizar cualquiera de las dos, deben primero establecer una dirección IP estática en la consola.

DMZ significa, en breve, que abren todos los puertos y protocolos en pro de comunicarse con un determinado dispositivo con una dirección IP específica. Así que dicho dispositivo se sitúa dentro de una zona desprotegida o si lo prefieren una área no restringida. DMZ es demasiado riesgoso!

La alternativa (la que prefiero): es configurar manualmente sólo el par de puertos & protocolos que verdaderamente se necesitan para la conexión para un determminado dispositivo con una dirección IP estática (para el caso que nos ocupa, la dirección IP de la 360).

Todo lo que tienen que hacer en la última opción, es seleccionar la lengüeta “Port Forwarding” en el router y configurar algo similar a:

  • Nombre de la aplicación (digamos, “XBox360Live”),
  • Cada rango de puertos (“de 80 a 80”, y así sucesivamente),
  • Los protocolos permitidos (UDP/TCP/Ambos),
  • La dirección IP local (esto es, el valor de la dirección IP estática de tu consola 360), y
  • Marcar “Habilitar” (o cualquier otra opción necesaria para activar dicha regla).

Lean este artículo para saber cuáles deben configurar.

Como mencioné al comienzo de este artículo, el problema antedicho fue el que no permitía que mi consola 360 se conectara a Xbox Live, apropiadamente. Para resumir, el archivo de configuración que había salvado tiempo atrás como respaldo no incluía estos valores. Solucionado!

Ahora bien, volviendo al tema …

Opcional: algúnos recomiendan (yo no) que cuando reciban un código de error como el 8007274c, deseleccionar una opción similar a “bloquear peticiones anónimas desde Internet” en el cortafuegos del router puede ayudar. Y agregan, en algúnos casos, limipiar la memoria chache de la consola (atención: hacerlo borrará todas las actualziaciones de software! Deberán descargarlas nuevamente) y/o verificar que los valores de DNS debidos estén configurados.

Por cierto, el reenvío de puertos sólo funciona para una aplicación por vez, lo cual significa que si dos aplicaciones en la red local intentan obtener acceso, digamos, a Internet usando el mismo puerto, un conficto ocurre y si el router no lo puede resolver, la conectividad sufre … talvez sean sus consolas 360 uno de los dispositivos en conflicto!

Si el cortafuegos de sus router tiene una vitácora (log), léanla para ver cual dispositivo y aplicación es la fuente del conflicto. también pueden intentar ver el log de los respecivos cortafuegos de los dispositivos, si tienen.

Si no pueden identificar qué es lo que está causando el conflicto en el puerto, supongo que desconectar todos los dispositivos, a excepción de la XBox 360, debería solucionar el problema de conexión, de manera que puedan jugar en línea nuevamente a algúnos videojuegos.

Obtienes algúnos mensajes extraños de error y/o pérdida de conexión al jugar algúnos videojuegos en línea en modo multijugador.

Si tienen una membresía Live Dorada vigente, entonces esto está emparentado con el tema de los puertos de conexión.

Algúnos juegos precisan que unos pocos puertos estén abiertos para ciertos protocolos (TCP/UDP/Both), los cuales difieren de los listados aquí.

De nuevo, si no desean establecer (la dirección IP de) sus consolas dentro del DMZ, entonces deberían configurar manualmente ambos, puertos más protocolos, para el juego.

Ok, cómo puedo saber qué par debería configurar? Bien, o bien pueden hacer una b´squeda en Internet para averiguarlo o visitar sitios como http://www.portforward.com/, los cuales tienen mucha información al respecto, para una lista de routers y servicios (incluyendo conexiones a la 360).

Otros problemas de conexión … más sus soluciones?

A modo de ejemplo, sus routers deben soportar una MTU mínima (“Unidad Máxima de Transmisión"”); la cual en el caso de XBox Live es: 1364. O el modem DSL de sus proveedores de Internet no es lo suficientemente bueno (soliciten su cambio).

Sería genial saber tanto el problema que enfrentaron al conectar su 360 a XBox Live como, por supuesto, la solución.

Bien, eso es todo. Espero que la información les sea de utilidad.

Dsifruten sus juegos!
~Pete

> Vínculo a la versión en inglés.

lunes, 27 de abril de 2009

INVARIANZA, COVARIANZA & CONTRAVARIANZA

Ultimamente, el mundo de los desarrolladores que utilizan C# está intrigado por el significado de dos palabras inusuales: covarianza y contravarianza.

Hay ahora una buena cantidad de artículos por ahí que intentan explicar los conceptos detrás de esas palabras príncipalmente a través de ejemplos, y porqué estos importan más para la versión 4.0 venidera de C#.

Para mencionar unos pocos artículos sobre el tema:

Puesto que este tópico es nuevo para muchos, y además, y seamos honestos aquí, NO ES TAN fácil de entender a primera vista, he decidido agregar mi granito de arena escribiendo otro artículo más (explicando la forma cómo yo lo entiendo) con la esperanza que eventualmente ayude a otros a comprenderlo de una buena vez.

Ok, suficiente introducción! Empecemos …

Desde un punto de vista de diseño, al querer referirnos a herencia, tendríamos que utilizar las palabras “generalización” y “especificación”. Pero en la práctica o en términos de implementación, usamos la palabra “herencia” en sí misma, más las siguientes dos palabras: “tipos base” y “tipos derivados”.

En lo que sigue, usaré el último grupo de términos a efectos de procurar un mejor entendimiento. Así que empecemos con unos conceptos básicos, ¿les parece?

(I) Lo Básico

En teoría, hablando de tipos por referencia, se establece que un tipo base is mayor a un tipo derivado porque puede contener una instancia de su propio tipo o bien una instancia de un tipo derivado.

Por lo tanto, un tipo derivado es menor que un tipo base pués el primero no puede contener a una instancia del último.

Esto es usualmente indicado como T >= S, siendo T el tipo base y S el tipo derivado. Por ejemplo,

Object >= String

Ahora bien, estamos en presencia de covarianza cuando la referencia a un objeto es declarada como su tipo real o como uno de sus tipos bases (y por “real” me refiero al tipo usado para crear la instancia del mismo, por ejemplo: “new Foo()”).

En otras palabras, un tipo derivado es covariante con su tipo base porque la dirección de asignación (en lo que sigue la llamaré “movimiento”), que va desde un tipo derivado a un tipo base, se permite por tanto que suceda.

Siguiendo este razonamiento, hablamos por ende de contravarianza cuando el “movimiento” se da en el sentido opuesto.

Y este es probablemente el concepto más difícil de visualizar. Esperemos que este ejemplo lo aclare: si tuviéramos un delegado que, digamos, tomara un string como parámetro de entrada y retornara un bool, entonces podríamos pasar un método que tome un object como parámetro de entrada y retornara el mismo tipo, en el ejemplo: un bool. ¿Por qué? Porque si podemos tratar con un tipo derivado dentro del método esperado etonces podremos también tratar específicamente con uno de sus tipos bases (veremos un ejemplo de ello más adelante).

Finalmente, hay un tercer concepto: si no se permite que suceda “ningún movimiento”, nos estamos refiriendo entonces a invarianza.

Por ejemplo, para prevenir conflictos de varianza, arreglos mutables “deberían” ser invariantes sobre el tipo base, siempre. Si estuviéramos creando un arreglo de objectos entonces no se nos debería permitir insertar, digamos, un string en el arreglo (covarianza). Y por ende, si estuviéramos creando un arreglo de strings no se nos debería permitir borrar una instancia de un objecto del mismo (contravarianza).

Ergo, de object >= string, podríamos inferir que object[] >= string[], SOLO SI ambos arreglos fueran inmutables.

Toda la explicación antedicha está en línea con el denominado Principio de Sustitución de Liskov, en especial en el sentido de que la forma en la cual se hace referencia a un objecto en un programa nunca debe alterar ningúna de sus propiedades (“deseables”).

(II) El Problema

En un lenguaje estático como C#, “type-safety” (seguridad de tipos) implica que el compilador es capáz de capturar errores de “transformación de tipos” (casting) en el código fuente en tiempo de compilación.

Como expliqué más arriba, los arreglos mutables deberían ser invariantes para así hacer valer la seguridad de tipos. Pero éste no es el caso de los arreglos en C#, los cuales son covariantes al tipo base. ¿Confundidos? Entonces prosigan con la lectura de los párrafos siguientes.

Esto significa que “un error” en el código fuente que debería ser capturado siempre por el compilador de C# ha sido convertido en una excepción que sólo puede ser detectada en tiempo de ejecución por el CLR!

¿Por qué? Para responder esta pregunta y de aquí en más definamos tres clases que últimamente a muchos parece gustarle utilizarlas en ejemplos de código sobre la materia: Animal, Gato y Perro, siendo Gato y Perro ambos tipos derivados de la misma clase base: Animal.

¿Y bien? Siempre pueden almacenar un Gato en un arreglo de Animales, pero con la covarianza de arreglos el tipo real a almacenar podría ser un arreglo de Perros.

Tomemos como ejemplo el siguiente código:

   1: Animal[] animales = new Perro[10];
   2: animal[1] = new Gato();

¿Qué esperan que pase en el código anterior? Que posiblemente el compilador detecte un error aquí, ¿cierto? ¿Cierto?!

Errado! Créanlo o no, dicho código sólo lanza una excepción en tiempo de ejecución en vez de un error en tiempo de compilación, en cualquiera de las versiones de C#!

Si esto está mal, ¿por qué se permite entonces? El tema es que el compilado sabe que ambos tipos derivados son Animales, y por ende se permiten las conversiones implícitas (implicit casting) para arreglos que declaran esperar Animales, incluso a pesar que la instancia real declara contener un tipo derivado totalmente distinto pero con un mismo tipo base común (en el código de ejemplo más arriba: Perro[10]).

Recuerden que en C# existe conversión implícita (implicit casting) de un tipo derivado hacia su tipo base porque es una operación segura en los tipos, pero lo mismo no aplica en el sentido opuesto. Y por ello, siempre deben usar la conversión de tipo explícita para revertir un tipo base al tipo derivado.

Desafortunadamente, este problema existe en C# desde su versión 1 y permanecerá para la versión 4 (shhhhh! … no lo digan en voz alta, pero es algo relacionado a Java).

Pero vayamos a lo que si va a cambiar …

En la sección anterior hablé de delegados. Pues bien, en C#, los delegados son covariantes para tipos de retorno de referencia y también contravariantes para parámetros de tipos de referencia. ¿Confundidos de nuevo? Sólo sigan leyendo …

Continuando con esta cosa animal, miremos el siguiente ejemplo para covarianza:

   1: public delegate Animal MiDelegado(int i);
   2:  
   3: MiDelegado miDelegado = new MiDelegado(MiMetodo);
   4:  
   5: public Gato MiMetodo(int i) { … }

Si la declaración de un delegado pide un método que espera por un Animal como tipo de retorno, el compilador sabe que un Gato es un Animal y dada la conversión implícita de un tipo derivado hacia su clase base, recibirán un Animal.

Sin embargo, lo opuesto no es cierto. Si el tipo de retorno es un Gato, no pueden pasar un Animal en cambio sin una conversión explícita al tipo Gato. ¿Por qué? Debido al hecho que un objeto Animal podría en realidad haber sido concebido como un Perro!

Ahora vayamos a un ejemplo de contravarianza en los parámetros de los delegados:

   1: public delegate int MiDelegado(Gato myCat);
   2:  
   3: MiDelegado miDelegado = new MiDelegado(MiMetodo);
   4:  
   5: public int MiMetodo(Animal miAnimal) { … }

Si la declaración de un delegado pide un método que espera a un Gato como parámetro, entonces pueden manejar su clase base. ¿Por qué? Debido a que el Gato es un Animal y por herencia Uds. saben como tratar con el como un Animal. Así que todo aquello que pueda ser asignado como un Gato puede pasarse y ser tratado específicamente como un Animal con seguridad de tipos, siempre y cuando el delegado declarado y el método pasado, ambos retornen el mismo tipo (en el ejemplo anterior, un entero).

Entendido. Pero, ¿por qué es ésto contravarianza? Estamos en realidad cambiando el sentido en la dirección de asignación puesto que estamos pasando un método que toma a un Animal como parámetro a un puntero de función (o delegado) que espera a un Gato como argumento.

¿Recuerdan la definición de contravarianza? Aquí estamos pasando un tipo mayor a uno menor. Así que estamos cambiando el sentido en la dirección de asignación.

Como en el ejemplo de la covarianza, lo opuesto no es verdad. Si el delegado espera a un Animal como parámetro, no podrán tratarlo como un Gato en el método pasado, puesto que no hay garantía alguna que el parámetro que se pasó como Animal sea un Gato. Podría ser un Perro, en cambio.

Un gran ejemplo para entender ésto se relaciona con dos delegados que esperan métodos con parámetros de los tipos MouseEventArgs y KeyEventArgs; ambos podrían en cambio hacer referencia a un único método que esperara un parámetro del tipo EventArgs. ¿Lo cuál significa? En tal situación, en ambos casos se espera el mismo comportamiento. Así que , como no les importaría la funcionalidad agregada de las dos clases “derivadas”, podrían tratar con ambos argumentos igualitariamente usando el tipo base en común.

Ok, si esto funciona para delegados en versiones actuales de C#, ¿donde está está el problema a arreglar entonces? Bien, la regla antedicha no es aplicable cuando almacenamos delegados genéricos, los cuales son siempre invariantes en C# 3.0, tanto para parámetros cuanto para tipos de retorno (lo mismo aplica para interfaces).

Dos ejemplos:

  • No pueden retornar un IEnumerable<string> si el método retorna IEnumerable<object> (ésto sería, covarianza), y
  • No pueden usar un delegado del tipo de Action<object> para reemplazar un delegado del tipo de Action<string> (ésto sería pués, contravarianza). Por favor fíjense que me estoy refiriendo aquí a asignar, digamos:
   1: Action<Gato> myDelegate = new Action<Animal>
   2: ( myAnimal => myAnimal.HacerAlgoConElAnimal() );

Y no a algo como –lo cual por cierto funciona bien:

   1: Action<Gato> miDelegado = MiMetodo;
   2:  
   3: public void MiMetodo(Animal miAnimal) { … }

Y si, por favor! Inténtenlo con C# 3.0 si dudan de mis palabras.

(III) La Solución

Y, ¿cuál es todo este lío con el nuevo uso agrgado a las palabras reservadas existentes "in" y "out" en el venidero C# 4.0?

Para tipos de referencia:

  • in = contravarianza (sólo pasar argumentos),
  • out = covarianza (sólo retornar tipos).

De los ejemplos anteriores:

  • Covarianza: IEnumerable<T> se convertirá en IEnumerable<out T>, así que un delegado que declara retornar un IEnumerable<object> de hecho podrá retornar un IEnumerable<string>, y
  • Contravarianza: con Action<in T>, podrán asignar un delegado del tipo Action<object> cada vez que esperen uno de Action<string>.

Les aconsejo dar un vistazo a los artículos listados al principio de este artículo en busca de ejemplos de código completos sobre este tema.

Fiú, eso es todo! Como pueden ver, la teoría detrás de estos conceptos no es tan fácil de entender pero tampoco tan difícil, así que espero que realmente hayan encontrado esta explicación útil para culminar la tarea de comprensión

Hasta la próxima,
~Pete

> Vínculo a la versión en inglés.

miércoles, 25 de marzo de 2009

SOLUCION PARA IE8 & ADOBE FLASH PLAYER

[ Si piensas que este artículo es útil, por favor considera descargar mi juego "Just Survive XP" en el Appstore (gratis por tiempo limitado):

=> http://itunes.apple.com/app/just-survive-xp/id461876025 ]

Ayer quería ver un video en Youtube cuando de repente, para mi sorpresa, en cambio sólo me encontré con el siguiente mensaje:

Hello, you either have JavaScript turned off or an old version of Adobe's Flash Player. Get the latest Flash player.

Puesto que Javascript estaba habilitado, a fin de comprobar si esto era un problema únicamente del sitio de Youtube, navegué a otros sitions de los cuales sabía que utilizaban tecnología Flash y me encontré que realmente era yo quien no podía ver contenido Flash. Todos los sitios me requerían que descargara e instalara la versión más reciente de Adobe Flash Player.

Así que visité el sitio de Adobe e intenté obtener en línea el último Flash Player. El asunto es que luego de instalar el componente, el clip de video que siempre aparece diciendo que tu instalación fue exitosa no apareció para nada. Es extraño que al refrescar la página -pulsando la tecla F5- luego si apareció. Raro.

Así que me dije, tal vez fue una falla menor, y abrí la página sitio del video en Youtube. Para mi desgracia, ese mensaje desagradable volvió a aparecer.

Esta vez, fui al menu "Herramientas" de IE8, y seleccioné la sección "Manage Add-Ons". Ahí, me encontré con que el componente "Shockwave Flash Object" estaba de hecho instalado y su versión era la correcta, así que intenté "resetearlo" desactivándolo, cerrando el navegador, reabriendo el navegador y reactivando el componente. Sin suerte!

Un último movimiento desesperado: por las dudas, sólamente abrí el siguiente menú de mi navegador:

"Herramientas -> Opciones de Internet -> General -> Eliminar"

y borré todo. Luego, descargué el instalador "fuera de línea" (off-line) de Adobe Flash Player desde el siguiente hipervínculo:

http://www.adobe.com/support/flashplayer/ts/documents/tn_19166/Install_Flash_Player_10_ActiveX.zip

Y finalmente, cerré el navegador y ejecuté dicho instalador (por cierto, dado que el instalador off-line se toma el trabajo de desinstalar cualquier version previa del complemento, me saltié esa parte y lo dejé hacerlo por mí).

Cuando el proceso terminó, sólo reabrí IE8, visité otra vez Youtube, y esta vez todo simplemente funcionó bien!

No se si esta solución alternativa funcionará para Uds., pero si por una de esas casualidades tienen una experiencia similar, entonces inténtelo a ver que pasa.

Bien, eso es todo por hoy. Espero que este artículo les sea de utilidad.

No cambien de canal,
~Pete

> Vínculo a la versión en inglés.

lunes, 23 de marzo de 2009

FALLA REINICIANDO WINDOWS AL INSTALAR IE8

Ayer estaba instalando la versión final de Internet Explorer 8 en mi máquina con Windows Vista Business y todo iba bien hasta que el sistema operativo me solicitó que reiniciara.

Puesto que en general esta es una solicitud usual para nuevas instalaciones de IE, pulsé sobre el botón "Reiniciar" con confianza.

Aquí se produjo el punto de inflexón de mi experiencia de instalación: luego que la máquina se reinició y los chequeos POST pasaron con éxito, la pantalla del monitor se quedó completamente en blanco y el disco duro principal sencillamente dejó de funcionar!!! Ni un sonido, ni un lead parpadeando, nada.

En otras palabras, el sistema operativo no podía iniciar; y créanme en esto: apagar y prender la máquina luego de un rato no funcionó para nada.

Si esto les llega a suceder, no tengan pánico. Por algúna razón desconocida -al menos, para nosotros los meros mortales, el "Master Boot Record" (MBR) del disco duro (HDD) debe haber sido sustituido durante la instalación de IE8.

Talvez utilizando los discos de instalación de tu sistema operativo pudiera funcionar, por ejemplo seleccionando la opción "Reparar" y dejando a la herramienta hacer su magia. Pero en mi caso, opté por un atajo.

No puedo asegurarles que el método que voy a describir funcione para Uds.; sólo puedo decirles que funcionó para mí, así que USEN EL METODO A SU PROPIO RIESGO!

Han sido advertidos ahora, por lo que sigan leyendo:

Para resolver esta situación no tienen porqué reinstalar Windows (dado que aún está por ahí en el disco duro); la única cosa que deben hacer es encontrar una manera de reescribir en su HDD el MBR apropiado.

Para lograr dicha tarea, busqué una vieja herramienta llamada Max Blast, la cual solía ejecutar cuando quería preparar discos duros (Maxtor o Seagate) para una nueva instalación de Windows o "reparar" un disco con fallas. En este caso en particular, mi HDD principal era Samsung pero igual quería probar suerte.

Encontré en mis archivos un viejo disco Floppy con la version 4 de Max Blast, así que usé ese disco para intentar reiniciar el sistema.

Luego, cuando Max Blast se ejecutó entré a la sección titulada "Herramientas de Mantenimiento" del menú principal y entonces seleccioné la opción "Actualizar MBR". Luego de elegir el HDD objetivo y de esperar por el término del proceso, finalmente reinicié mi máquina para ser testigo de que la solución funcionó totalmente!!!

Windows Vista otra vez iniciaba bien y IE8 estaba completamente instalado. Todo simplemente volvió a la normalidad. Y sólo me tomó al rededor de 3 minutos arreglarlo.

Bueno, esto es todo. Espero que este artículo les sea de utilidad ... si algúna vez se encuentran, por esas cosas, en una situación similar :(

Hasta la próxima!
~Pete

> Vínculo a la versión en inglés.

viernes, 13 de febrero de 2009

XNA Y METODOS DE EXTENSION - PARTE 3

El artículo original fue escrito el 17-Nov-08.

Con este tercer artículo, estoy culminando mi serie introductoria al mundo de los métodos de extensión y XNA.

Al principio, este artículo se centraría en cómo extender la clase "Random" para obtener nuevos colores, pero "Ziggy", en su reciente artículo titulado "Particles Using LINQ" -el cual vale la pena leer, por cierto- ha mostrado con claridad cómo lograrlo, así que tomaré ese gran artículo como punto de partidapara profundizar en la discusión del tema.

Si ves el método de Ziggy llamado "RandColor" -al cual me referiré en lo que sigue como "NextColor" para estar en línea con la clase Random, el da muestra de una de las nuevas características del Framework 3 de XNA: cuando usas "floats" no hay necesidad de crear un "Vector3" para luego crear un "Color", no más.

En previas versiones de XNA, para usar floats a fin de cear un color, debías declarar:

public static Color NextColor(this Random rand)
{
    return new Color(new Vector3(rand.NextFloat(), rand.NextFloat(), rand.NextFloat()));
}

Pero desde el XNA Framework 3 en adelante, lo siguiente también funciona:

public static Color NextColor(this Random rand)
{
    return new Color(rand.NextFloat(), rand.NextFloat(), rand.NextFloat());
}

Ahora bien, si ves la clase "Particle2D" en el artículo de referencia, entonces notarás el campo llamado "Color", el cual es declarado como una variable que comienza en mayúscula; un nuevo método de extensión podría ser creado para dicho campo, como sigue:

public static void NextColor(this Random rand, out Color color)
{
    color = new Color(rand.NextFloat(), rand.NextFloat(), rand.NextFloat());
}

Por ende, podemos usarlo dentro del método de actualización "Update" en el referido ejemplo, reemplazando la siguiente línea:

p.Color = rand.NextColor();

Por esta nueva línea:

rand.NextColor(out p.Color);

Dado que el tipo "Color" es un estructurado (struct), la ventaja de usarlo sobre la primer opción podría considerarse en general como marginal, pero eventualmente te encontrarás con la situación donde incluso una mejoría mínima en tu código es deseada, especialmente cuando te manejas con el compact framework.

Nueva pregunta: que tal si sigues la guía de diseño de C#, declarando el campo "color" en minúscula como un campo privado y así mismo, incluyes un "getter" y un "setter" en la clase, en la forma de una propiedad pública llamada "Color"?

En ese caso, la nueva sobrecarga no puede usarse dado que Propiedades e Indexadores no pueden pasarse como parámetros "out" y/o "ref". Por qué? Bueno, simplemente recuerda que cuando usas propiedades e indexadores no estás tratando con variables sino métodos. En este caso particular, la primera implementación -esto es, la cual se incluye en el artículo de Ziggy- sería la mejor solución.

Reconozcámoslo! Podrías estar tentado a pasar la instancia existente de "Particle2D" como "ref" para salvar la restricción antedicha, tal como sigue:

public static void NextColor(this Random rand, ref Particle2D particle)
{
    particle.Color = new Color(rand.NextFloat(), rand.NextFloat(), rand.NextFloat());
}

Debería compilar bien PERO SOLO si NO PASAS a ese método la variable temporal creada por un bucle "foreach" -en contraposición al ejemplo base. Esta operación simplemente NO ESTA PERMITIDA para las variables de iteración de bucles "foreach". Mi consejo aquí es simple: evita esta tentadora implementación y como dije antes sólo utiliza lo presentado por Ziggy. En general, la solución más simple es la correcta.

Una nota al márgen: en caso que seas nuevo con C#, es importante notar que en este caso estamos utilizando "ref" en vez de "out", puesto que necesitamos una instancia existente (esto es, no nula) de la clase a fin de asignar el nuevo color. De lo contrario, al usar "out" tendríamos que crear una instancia del tipo "Particle2D" desde dentro del método de extensión mismo ANTES de asignar el nuevo color al mismo.

Ahora bien, pasemos a una situación interesante. Que tal si:

  1. No estamos ni estaremos dentro de un bucle "foreach",
  2. No queremos exponer la totalidad del tipo "Particle2D" a un método de extensión, y al mismo tiempo
  3. Precisamos usar este método para aquellas clases donde estén declaradas propiedades de asignación de colores ("setters")?

Si ese es el caso, entonces deberías echarle un vistazo a la siguiente implementación:

public interface IColor
{
    Color Color { get; set; }
}
 
public static class HelperMethods
{
 
    ...
 
    public static void NextColor<T>(this Random rand, ref T instance)
        where T : class, IColor
    {
        instance.Color = new Color(rand.NextFloat(), rand.NextFloat(), rand.NextFloat());
    }
}

Como puedes ver, estamos aqui combinando el poder de "Generics" con métodos de extensión.

Pero, por qué la restricción "IColor"? Lo que estamos diciendo aquí es simple: únicamente tipos que implementen la propiedad "Color" pueden llamar a este método de extensión. Y aquellas instancias no nulas pasadas al método como parámetro (recuerda que estamos utilizando la palabra "ref" aquí) serán manejados bajo el tipo "IColor".

Ok, pero por qué la restricción "class" adicional? Pregunta engañosa. Como corolario de la anterior restrcción, sin no especificamos que sólo se aceptan instancias de clases como parámetros, entonces podrían además pasarse estructurados al llamar al método, y para ello también deberán implementar la interface "IColor" ... Lo cual siginifica? ... Tic tac tic tac ... Pseee, "boxing". Ummm! Esa palabra desagradable. Manejar estructurados mediante interfaces causa "boxing". Da miedo, no? Ahora ya lo sabes, en caso que no lo supieras.

Para resumir, como puedes ver los métodos de extensión son muy prácticos, pero tienes que usarlos con cuidado si quieres evitar ingresar en una trampa de diseño. Como es habitual, no todas las situaciones pueden resolverse de la misma forma, incluso si al principio te suena lógico. Puedes encontrarte diciendo "Quéeee? Pero por quéeee? ...". Así que espero que estos ejemplos te muestren qué usar y cuándo, y qué evitar y porqué.

Algunos pensamientos finales sobre métodos de extensión:

  1. Son fenomenales para tareas habituales: créelo o no, dos tipos comúnes donde siempre me encuentro repitiendo tareas usuales son las clases "TYPE" y "RANDOM", pero esta bondad puede ser utilizada para tantos tipos como precises,
  2. Son de utilización intuitiva: antes de que esta funcionalidad fuera introducida en el .NET Framework, uno usualmente escribía clases estáticas llenas de ayudantes ("helpers"); por favor no me malentiendas, aun tienes que hacerlo, pero ahora estos ayudantes se presentan en una forma más intuitiva y amigable al usuario. Con los métodos de extensión, no tienes que buscar métodos "navegando" la clase estática donde los declaraste e implementaste; en cambio, únicamente miras las instancias de los tipos que estés utilizando. Y por último pero no menos importante,
  3. Cumplen con los principios de diseño: como dije en mi primer artículo de la serie, no estamos rompiendo ningún principio de diseño, debido a que no contamos con acceso directo a los miembros privados de los tipos "extendidos".

En resúmen, si utilizas métodos de extensión con cuidado y sabiduría, pueden convertirse en un amigo verdadero a la hora de programar tu juego u aplicación basada en XNA: simplemente te dan una mando para entender cualquier tipo con nuevos métodos COMO SI éstos originalmente fueran parte de dichos tipos, lo cual a su vez puede volverse útil, por ejemplo:

  1. Cuando no quieres crear una especificación de una clase, o por otro lado,
  2. Cuando quieres agragar mas funcionalidad dentro de una clase sellada ("sealed") de alguna manera -hasta cierto punto, claro está.

Así que vamos, prueba los "Extension Methods" y comparte lo que encuentres con la Comunidad XNA. Sería grandioso y divertido saber para que los estas utilizando!

Bueno, espero que hayas encontrado esta serie útil. Comentarios y sugerencias constructivas son siempre bienvenidas.

Otra ronda, salud!
~Pete