Saltar al contenido

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

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

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

¡Microsoft MVP!
Mostrando entradas con la etiqueta c#. Mostrar todas las entradas
Mostrando entradas con la etiqueta c#. Mostrar todas las entradas
martes, 17 de abril de 2018
C# Va un truquillo muy rápido que, aparte de al que os habla, quizás a alguno más os provoque un momento “ahh, pues claro, cómo no se me había ocurrido antes” ;)

Y es que el problema con las costumbres es que son difíciles de quitar; por ejemplo, probablemente muchos estamos acostumbrados a obtener el nombre de los miembros de un enum de la siguiente forma:
public enum Color { Red, Green, Blue }
static void Main(string[] args)
{
    Console.WriteLine(Color.Red.ToString()); 
    // Output: Red
}
A priori es correcto: llevamos años haciéndolo así y no nos ha ido mal del todo. Bueno, o sí, pero probablemente no por este motivo ;)
martes, 25 de abril de 2017
C#Pues ya con la séptima versión de C# en la calle, aparecida de la mano del flamante Visual Studio 2017, va siendo hora de echar un vistacillo rápido a las principales novedades que encontraremos en esta nueva iteración de nuestro lenguaje favorito.

Y hoy vamos a comenzar con las funciones locales, una nueva capacidad que nos permitirá crear funciones locales a un ámbito, y que no serán visibles desde fuera de éste. Por ejemplo, podemos crear funciones en el interior de métodos, constructores, getters o setters, etc., pero éstas sólo serán visibles desde el interior del miembro en el que han sido declaradas.

Puede ser útil en determinados escenarios, puesto que evitan la introducción de "ruido" en las clases cuando determinado código sólo se va a consumir en el interior de un método, y al mismo tiempo pueden contribuir a mejorar la legibilidad y robustez del código.
miércoles, 29 de marzo de 2017
Desarrollador volviéndose loco
Imagen original de Pixabay.
La llegada del próximo April Fool's day, algo parecido a lo que por aquí conocemos como el día de los Inocentes, me ha recordado una curiosidad con la que me topé hace algún tiempo que, si tenéis un poco de maldad reprimida, podéis utilizar para gastar una broma a algún compañero desarrollador y echar unas risas.

La historia consiste en abusar del amplio conjunto de caracteres soportado por UTF, sustituyendo el punto y coma de finalización de una línea de código (";") por el símbolo de interrogación griego (";", Unicode 037E), indistinguibles a simple vista, como en la siguiente línea:
public void HelloWorld()
{
    Console.WriteLine("Hello world!");
}
martes, 14 de julio de 2015
C#Desde luego, Stackoverflow, además de salvarnos la vida en numerosas ocasiones, es una fuente infinita de curiosidades, y hoy vamos a ver una que me ha llamado la atención últimamente, aunque sea un tema que lleva circulando por la red muchos años.

El asunto era que un usuario que quería saber cuál era el nombre del operador “-->” existente en muchísimos lenguajes, como podéis comprobar en un código perfectamente válido y compilable en C# como el siguiente:
static void Main(string[] args)
{

    int x = 10;
    while (x --> 0) // while x goes to 0
    {
        Console.Write(x);
    }
    // Shows: 9876543210
}

¿Y cómo se llama ese operador? No puedo negar que al principio me quedé un poco descolocado, como probablemente os haya ocurrido a algún despistado más, pero al leer las respuestas el tema quedaba bien claro que es sólo una cuestión de legibilidad de código, o mejor dicho, de falta de ella, acompañado de un comentario algo desafortunado que incita a la confusión. Complejidad innecesaria en cualquier caso.

En realidad, se trata de dos operadores seguidos, autodecremento y comparación, utilizados de forma consecutiva y abusando de las reglas de precendencia. Sin duda habría quedado mucho más claro de cualquiera de estas formas:
// Mejor: usar los espacios apropiadamente
while (x-- > 0) 
{
    Console.Write(x);
}

// Mucho mejor: usar paréntesis para dejar explícitas las precendencias
while ((x--) > 0) 
{
    Console.Write(x);
}

// Lo más claro: no liarse con expresiones complejas
while (x > 0) 
{
    x--;
    Console.Write(x);
}
WFTs/minutoEste tema lo publicó también Eric Lippert hace algunos años como broma del fool’s day, anunciando un operador que habían añadido a última hora a C# 4.0, aunque al parecer es algo que ya circulaba antes por la red.

En fin, lo que me resultó curioso y quería mostraros en este post es cómo unos simples espacios pueden hacernos ver operadores donde no los hay, introducirnos dudas incluso en algo tan conocido como son los operadores de nuestro lenguaje de programación favorito, y hacer que de un vistazo no entendamos el código que tenemos delante.

Pero ya sabéis: si queréis parecer muy inteligentes y aumentar vuestro ratio de WTFs/minuto en la próxima revisión de código, no dudéis en usarlo ;)

Y si queréis ser ya los reyes de vuestro equipo, tampoco dudéis en usar el operador inverso “<--" que sube un peldaño adicional en el nivel de absurdo:
static void Main(string[] args)
{

    int x = 10;
    while (0 <-- x) // while 0 is approached by x
    {
        Console.Write(x);
    }
    // Shows: 987654321
}

Publicado en Variable not found.
martes, 5 de mayo de 2015
C# 6Ya jugando con Visual Studio 2015 y C# 6, estoy comprobando que la interpolación de cadenas es una de las características más interesantes y prácticas de las novedades que presentará la nueva versión del lenguaje, porque no son pocas las ocasiones en las que creamos expresiones concatenando literales de cadena y variables, o que hacemos uso de String.Format() para construir strings más complejos.

Hace unos meses ya adelantamos por aquí sus principales características, aunque aún era algo pronto para poder probar en profundidad esta nueva y esperada feature. Ahora, más avanzado ya su desarrollo, ha llegado el momento de echarle otro vistazo más en profundidad y ver cómo queda finalmente (o casi finalmente, todavía podría cambiar algo!).

En este post vamos a ver rápidamente los puntos principales a tener en cuenta para dominar esta nueva característica que sin duda facilitará nuestro trabajo y nos hará más productivos.
martes, 25 de noviembre de 2014
imageSeguimos con esta serie sobre las novedades de C# 6, y esta vez le toca el turno a una solicitada característica que recientemente ha entrado a formar parte de las elegidas para formar parte de esta nueva versión de nuestro lenguaje favorito: la interpolación de cadenas.

Dado que aún se encuentra en desarrollo no es posible probarlo de forma sencilla, por lo que de momento tendremos que conformarnos con sacar conclusiones basándonos en la discusión de diseño en Codeplex y de la documentación existente. Avisan además que la especificación puede ser modificada, por lo que todo lo que digamos aquí aún puede variar algo, aunque mayormente será (espero) válido.
martes, 18 de noviembre de 2014
imageSeguimos hablando de las novedades de C# 6, y en esta ocasión nos centraremos en una interesante característica cuyo nombre, sinceramente, no estoy seguro de ser capaz de traducir de forma correcta del original “Expression bodied function members”. Quizás sería algo así como “expresiones en cuerpo de miembros de función”, pero bueno, lo importante no es el nombre sino el concepto, así que vamos a centrarnos en él.

martes, 28 de octubre de 2014
imageSeguimos con la serie donde vamos desgranando las novedades de C# 6, y en esta ocasión vamos a ver algunas mejoras en el uso de bloques try/catch, a las que seguro podremos sacar buen partido.

En este post trataremos dos temas distintos. En primer lugar, comentaremos la introducción del soporte de await en bloques catch/finally, y seguiremos con la nueva capacidad de filtrado de excepciones.

Vamos a ello :-)
martes, 21 de octubre de 2014
image33En otras ocasiones hemos hablado de novedades que acompañarán a la próxima revisión de C# que nos ahorraban algo de tiempo y pulsaciones de tecla a la hora de codificar algunos escenarios bastante frecuentes, a la vez que mejoraban la legibilidad.

Pues en esa misma línea, vemos ahora una nueva forma de inicializar colecciones clave-valor, como son los diccionarios. Ciertamente es un cambio bastante pequeño, de los que fácilmente podrían pasar desapercibidos junto a otras novedades de mayor peso, pero creo que también vale la pena conocerlo.
jueves, 16 de octubre de 2014
imageHace unos días mostrábamos por aquí el nuevo operador nameof que acompañará a C# 6, y cuya función, resumidamente, es obtener el nombre a nivel de código de la variable o miembro a la que se aplica:



En los comentarios de este post, el amigo Kiquenet se preguntaba qué incidencia podía tener el uso de este operador en el rendimiento de nuestras aplicaciones, y esa duda es la que vamos a responder muy rápidamente ahora.
martes, 7 de octubre de 2014
imageCuando los lenguajes evolucionan, no todas las novedades son espectaculares ni revolucionan la forma de escribir nuestro código. A veces la cosa va simplemente de añadir pequeños detalles que nos hacen la vida más fácil y ayudan a evitar errores.

Y hoy hablaremos de uno de ellos: el nuevo operador nameof, que acompañará a C# a partir de su versión 6.
martes, 30 de septiembre de 2014
C#
[Actualizado 16-Nov-2014] El equipo de diseño del lenguaje C# ha decidido finalmente no incluir esta característica en la versión 6. En fin, fue bonito mientras duró :(

Seguimos comentando novedades que encontraremos en la próxima versión de C#, y en esta ocasión vamos a ver una nueva característica que, sin resultar espectacular, sí nos va a proporcionar una fórmula más cómoda y concisa para resolver algunos escenarios bastante habituales.

Seguro que os resulta familiar el siguiente código, más que nada porque lo habréis escrito decenas o cientos de veces:


martes, 23 de septiembre de 2014
En posts anteriores hemos hablado del operador de navegación segura “.?” y de la inicialización de propiedades automáticas, y vamos a comentar ahora otra de las novedades de la versión de C# que viene en camino, pero que en esta ocasión no tengo claro que sea un avance en la dirección correcta. Os lo cuento y ya decidís vosotros ;-)

Hay escenarios en los que en el interior de una clase utilizamos de forma intensiva miembros estáticos de otras clases. Un ejemplo habitual podemos encontrarlo en componentes que hagan mucho uso de System.Math para la realización de cálculos matemáticos, o incluso en el conocido System.Console:




martes, 16 de septiembre de 2014
Novedades de C# 6.0Hace algún tiempo comentamos alguna de las novedades que se incluirán en la próxima versión de C#, concretamente el nuevo operador de navegación segura “?.”, pero la cosa no acaba aquí: se avecinan un buen número de cambios que prometen seguir mejorando nuestro lenguaje favorito :-)

Por ejemplo, una nueva característica que tendremos disponible es la inicialización de propiedades automáticas, algo que antes también podíamos hacer, aunque de forma menos directa.
martes, 4 de marzo de 2014

El operador “?.”, también llamado safe navigation operator, era la característica más demandada para el lenguaje C# en Uservoice, uno de los principales canales utilizado por Microsoft para obtener feedback e ideas a aplicar en nuevas versiones de sus productos, y parece que definitivamente se está considerando la posibilidad de incluir este útil azucarillo sintáctico tanto en C# como en VB.

Para los que no lo conozcáis, se trata de una construcción que permite acceder a propiedades de objetos sin temor a las null reference exceptions lanzadas cuando estos objetos son nulos.
lunes, 31 de mayo de 2010
.NET Los parámetros opcionales son una interesante ayuda que hace tiempo que está presente en otros lenguajes como Visual Basic .NET, y ha sido introducida en la versión 4.0 de C#, para alegría de muchos.

A grandes rasgos, esta característica nos permite especificar valores por defecto para los parámetros de nuestros métodos, ahorrándonos tiempo de codificación:

class Program
{
    public static void Main(string[] args)
    {
        Muestra();            // Imprime 1,1
        Muestra(8);           // Imprime 8,1
        Muestra(3,4);         // Imprime 3,4
        Console.ReadKey();
    }
 
    static void Muestra(int a=1, int b=1)
    {
        Console.WriteLine(a + "," + b);
    }
}

Desde siempre, ignorante de mí, había pensado que esto no era más que una triquiñuela del compilador, un azucarillo sintáctico destinado a facilitarnos la creación de sobrecargas de forma rápida, pero, ahora que lo he estudiado algo más a fondo, resulta que no es así. De hecho, los parámetros opcionales están soportados a nivel de plataforma, y funcionan de forma algo extraña (o al menos diferente a lo que podía esperarse), por lo que es conveniente conocerlos bien para no cometer determinados errores.

En primer lugar, me ha llamado la atención que la detección de la ausencia de parámetros en la llamada y la asignación de los valores por defecto no la realiza el método en el que se han definido. Es decir, sobre el ejemplo anterior, no es el método Muestra() el que comprueba si se han suministrado valores para los parámetros a y b, ni el que le asigna los valores por defecto en caso contrario. Esta "comprobación" se realiza en tiempo de compilación (!).

Esto lo demostramos muy fácilmente si descompilamos esta misma aplicación con ayuda del imprescindible Reflector, que nos mostrará el siguiente código:

class Program
{
    public static void Main(string[] args)
    {
        Muestra(1, 1);
        Muestra(8, 1);
        Muestra(3, 4);
        Console.ReadKey();
    }
 
    public static void Muestra([Optional, DefaultParameterValue(1)] int a, 
     [Optional, DefaultParameterValue(1)] int b)
    {
        Console.WriteLine(a + ", " + b);
    }
}

Como se puede observar, se ha generado un método Muestra() cuyos parámetros incluyen atributos que indican su opcionalidad y el valor por defecto en cada caso.

Pero lo curioso está en el método Main(), desde donde se hacen las llamadas, el que podemos observar que las invocaciones a Muestra() incluyen valores para todos los parámetros, como si se tratara de constantes especificadas directamente por el desarrollador.

Por tanto, no hay nada mágico en los métodos con parámetros opcionales, ni sobrecargas, ni código de comprobación o asignación insertado de forma automática. Es el propio compilador el que, en el momento de generar el código IL, extrae los valores por defecto de los parámetros no especificados en la llamada examinando los atributos presentes en la signatura y los introduce en la invocación.

Y es aquí justo donde hay que tener cuidado al utilizar los parámetros opcionales. Dado que el valor de los parámetros se determina en tiempo de compilación y se incluyen como constantes en el código IL generado, pueden producirse efectos no deseados si trabajamos con distintos ensamblados.

Veámoslo con un ejemplo, que, aunque poco real, creo que ilustrará un escenario donde los parámetros opcionales podrían jugarnos una mala pasada.

En el siguiente código, perteneciente al ensamblado LogicaNegocio.dll, vemos un método CalculaImporteconIva(), que retorna un importe aplicándole el impuesto (IVA) correspondiente:

public class LogicaNegocio
{
    public double CalculaImporteConIva(double importe, double iva = 0.16)
    {
        return importe + importe*iva;
    }
}

Así, podemos tener un ensamblado externo, pongamos ERP.exe, que haga uso de este método de la siguiente forma:

public void muestraDesglose(double importe)
{
    double importeTotal = logica.CalculaImporteConIVA(importe)
    // Mostrarlo...
}

En el momento de compilación de ERP.exe, la llamada anterior quedará en el ensamblado resultante igual que si hubiéramos hecho esta llamada:
 
    double importeTotal = logica.CalculaImporteConIVA(importe, 0.16)

Si ahora se produce una subida de IVA (como lamentablemente va a ocurrir en breve), acudiríamos a modificar el valor por defecto del parámetro iva en el método CalculaImporteConIva() y recompilaríamos LogicaNegocio.dll:

public class LogicaNegocio
{
    public double CalculaImporteConIva(double importe, double iva = 0.18)
    {
        return importe + importe*iva;
    }
}

Sin embargo, si no recompilamos ERP.EXE desde éste seguiríamos enviándole el valor anterior (0.16, recordad que este valor aparece como constante en el ensamblado), lo que podía provocar algún problema. Es decir, si queremos mantener la coherencia del sistema, nos veríamos obligados a recompilar todos los ensamblados que referencien LogicaNegocio.dll.

Conclusión: en métodos públicos, y especialmente en aquellos que serán consumidos desde ensamblados externos, es conveniente utilizar parámetros opcionales sólo cuando los valores constantes sean “verdades universales”, como las constantes matemáticas o datos que con toda seguridad no van a cambiar.  No es buena idea utilizarlos para reflejar valores variables o constantes de lógica de negocio, con posibilidades de cambio aunque sean remotas.

Por último, comentar que aunque este post está centrado en C#, todas estas precauciones son igualmente válidas para Visual Basic .NET.

Publicado en: Variable not found
miércoles, 21 de abril de 2010
¿Sómos los primeros en llegar al estadio? Casualmente encuentro en el post de Chris Eargle “Any() versus Count()” un tema del que pensaba escribir hace tiempo y al final dejé en el tintero: ¿cómo podemos determinar si una enumeración está vacía? Vale, es bien fácil, una enumeración está vacía si tiene cero elementos.

Si trabajamos con un array, podemos consultar la propiedad Length; si se trata de una colección, podemos utilizar la propiedad Count, que también nos devolverá el mismo dato de forma directa.

Sin embargo, cuando procesamos información es frecuente tratar con tipos de datos enumerables cuya implementación exacta desconocemos, y en los que no tenemos acceso a ninguna propiedad que nos permita conocer el número de elementos exactos que tiene almacenados. Por ejemplo, esto ocurre cuando obtenemos un conjunto de datos en forma de IEnumerable o trabajamos con LINQ sobre alguna fuente de información.

Pues bien, en estos casos hay que ser algo prudentes con la forma de consultar el número de elementos. Me he topado con código propio en el que, simplemente por despiste, utilizaba el método Count(), facilitado por LINQ para las enumeraciones y otras colecciones de datos, con objeto de saber si una lista estaba vacía:

var prods = services.GetProductsByCategory(category);
if (prods.Count() > 0)
{
   // Hacer algo con los elementos de la lista
}
else
{
   // No hay elementos
}

Seguro que ya os habréis percatado de que eso está mal, muy mal. El método Count() recorrerá uno por uno los elementos para contarlos, retornando el número exacto de ellos. En escenarios de un gran número de elementos, o cuando es costoso obtener cada elemento (como al traerlos de una base de datos) puede suponer un consumo de recursos enorme.

Y justo por esto existe el método Any(), que comprueba únicamente si existe al menos un elemento en la colección. Internamente este método itera sobre la colección retornando true cuando encuentra el primer elemento, por lo que es mucho más eficiente que el anterior:

var prods = services.GetProductsByCategory(category);
if (prods.Any()) // Mucho mejor :-)
{
  // Hacer algo con los elementos de la lista
}
else
{
  // No hay elementos
}

La utilización de Any() también es interesante para comprobar la existencia de elementos que cumplan una condición, expresada en forma de predicado; retornará true cuando encuentre el primer elemento que lo haga cierto:

if (pedidos.Any(p => p.Pendiente))
{
   // Mostrar alerta, hay al menos un pedido pendiente
}

Estamos en la puerta de un Estadio y queremos saber si vamos a ser los primeros en entrar al recinto… ¿le preguntaríamos al portero el número exacto de aficionados que ya están dentro para, si es cero, saber que somos los primeros? ¿O nos bastaría simplemente con preguntarle si hay alguien? ;-P

Publicado en: Variable not found
Hey: ¡estoy en Twitter!
miércoles, 4 de noviembre de 2009

Cadenas Encuentro en el blog de Gunnar Peipman un post sobre el nuevo método string.IsNullOrWhiteSpace, aparecido en .NET Framework 4.0 Beta 2, cuya misión es retornar un booleano indicando si la cadena pasada como parámetro contiene un nulo, está vacío, o exclusivamente caracteres de espaciado (espacios, saltos de línea, tabulaciones, etc.), un escenario bastante frecuente cuando estamos, por ejemplo, validando formularios o cualquier tipo de datos de entrada.

Por si no podemos esperar hasta la llegada de la nueva versión del framework, Gunnar propone una solución temporal basada en crear un método de extensión sobre la clase string que realice la misma tarea:

 
public static class StringHelper
{
    public static bool IsNullOrWhiteSpace(this string s)
    {
        if (s == null)
            return true;
 
        return (s.Trim() == string.Empty);
    }
}
 
Como ya comenté por aquí hace tiempo, la ventaja que tiene utilizar esta técnica es que permite invocar el método sobre una instancia de cadena, aunque ésta sea nula, sin riesgo a errores:
 
string a = null;
string b = "    ";
string c = "\n";
string d = "Hey!";
Console.Write (a.IsNullOrWhiteSpace()); // True
Console.Write (b.IsNullOrWhiteSpace()); // True
Console.Write (c.IsNullOrWhiteSpace()); // True
Console.Write (d.IsNullOrWhiteSpace()); // False

Y para los fieles a Visual Basic .NET, ahí va el código equivalente:

Imports System.Runtime.CompilerServices
Public Module StringHelper
    <Extension()> _
    Public Function IsNullOrWhiteSpace(ByVal s As String) As Boolean
        If s Is Nothing Then
            Return True
        End If
        Return s.Trim() = String.Empty
    End Function
End Module

Publicado en: Variable not found.

martes, 15 de septiembre de 2009

No es algo que ocurra muy frecuentemente, pero en determinadas ocasiones puede ser útil inicializar una propiedad de un tipo anónimo con el valor nulo, por ejemplo:

var conductor = new { Nombre = ”Marisa”, Edad = 34, Auto = “Renault Megane” };
var peaton = new { Nombre = “Juan”, Edad = 43, Auto = null };

En el código anterior se entiende que lo que queremos indicar estableciendo la propiedad Auto a null es que la persona que estamos representando no dispone de automóvil, o bien no conocemos este dato.

Algo muy normal… sino fuera porque el compilador se empeña en abofetearnos con el siguiente error:

Cannot assign <null> to anonymous type property

Y la verdad, en cuanto lo pensamos un poco, tiene sentido. En los casos anteriores, se está instanciando un tipo anónimo cuyas propiedades son generadas en tiempo de compilación. En el caso anterior, el compilador crearía para el tipo anónimo una propiedad “Nombre” de tipo string, una “Edad” de tipo integer, y una “Auto”, de tipo… ¿qué tipo podría asignar el compilador a esta propiedad? Ese es precisamente el problema.

Lo mismo ocurre al intentar inicializar variables implícitamente tipadas con el valor nulo; aunque cambia el error, el concepto idéntico, el compilador no es capaz de inferir el tipo de la variable:

var persona = null; // Error: Cannot assign <null> to an implicitly-typed local variable

La forma de solucionarlo en ambos casos es muy sencilla: basta con informar explícitamente al compilador del tipo de la propiedad, para que pueda crearla sin problema, por ejemplo así:

var peaton = new { Nombre = "Juan", Edad = 43, Auto = (string)null };
var persona = null as Persona;

Publicado en: Variable not found

lunes, 20 de julio de 2009

En la plataforma .NET existen distintas formas de hacer que una llamada a un método sea omitida bajo determinadas circunstancias. Por ejemplo, los métodos parciales permiten, en C# 3.0 y VB 9.0, que el compilador omita la llamada a funciones no implementadas. También existe la posibilidad de utilizar las clásicas directivas (como #if… #endif ) para incluir código cuando existan constantes de compilación.

Es menos conocida, sin embargo, la existencia del atributo ConditionalAttribute, que aplicado a un método hace que las llamadas a éste no sean incluidas en el ensamblado si no existe la constante de compilación cuyo nombre se indica en el parámetro.

Por ejemplo, explorando un poco el espacio de nombres System.Diagnostics, vemos que todos los métodos de la clase Debug, están adornados por este atributo así:

   1: [Conditional("DEBUG")]
   2: public static void WriteLine(string message)
   3: {
   4:     TraceInternal.WriteLine(message);
   5: }

Ahora, imaginad que tenemos un código que usa esta clase, por ejemplo de la siguiente manera:


   1: public static void Main(string[] args)
   2: {
   3:     for(int i=0; i < 1000; i++)
   4:     {
   5:         Debug.WriteLine("Iteración " + i);
   6:         ProcesaAlgoComplejo(i);
   7:         int j = ProcesaOtraCosa(i);
   8:         Debug.WriteLine("Obtenido " + j);
   9:         // ...
  10:     }
  11: }

Si compilamos en modo “debug” (o simplemente está definida la constante de compilación con dicho nombre), las llamadas a estos métodos serán omitidas en el ensamblado resultante, por lo que el código finalmente generado será totalmente equivalente a:


   1: public static void Main(string[] args)
   2: {
   3:     for(int i=0; i < 1000; i++)
   4:     {
   5:         ProcesaAlgoComplejo(i);
   6:         int j = ProcesaOtraCosa(i);
   7:         // ...
   8:     }
   9: }

Pero ojo, que se omiten tanto las llamadas al método como la evaluación de sus parámetros, y esto puede provocar errores difíciles de detectar. Por ejemplo, el siguiente código daría lugar a un bucle infinito de los de toda la vida ;-) si compilamos en modo “release”; sin embargo, compilando en “debug” funcionaría correctamente:

   1: int j = 0;
   2: while (j < 100)
   3: {
   4:     Console.WriteLine("Hey " + j);
   5:     Debug.WriteLine("Procesado " + (j++));  // <-- Esta línea desaparece cuando
   6:                                             //     compilamos en modo release!
   7: }

Aunque en el anterior ejemplo estamos jugando con la constante predefinida DEBUG, este atributo podemos utilizarlo con otras constantes, tanto existentes como personalizadas. Como muestra, podéis echar un vistazo a la definición de la clase System.Diagnostics.Trace, que vincula el uso de sus métodos a la existencia de la constante TRACE:


   1: public sealed class Trace
   2: {
   3:     // ...
   4:  
   5:     private Trace();
   6:     [Conditional("TRACE")]
   7:     public static void Assert(bool condition);
   8:     [Conditional("TRACE")]
   9:     public static void Assert(bool condition, string message);
  10:     [Conditional("TRACE")]
  11:     public static void Assert(bool condition, string message, string detailMessage);
  12:     [Conditional("TRACE")]
  13:     public static void Close();
  14:     [Conditional("TRACE")]
  15:     public static void Fail(string message);
  16:     [Conditional("TRACE")]
  17:     public static void Fail(string message, string detailMessage);
  18:     [Conditional("TRACE")]
  19:     public static void Flush();
  20:     [Conditional("TRACE")]
  21:  
  22:     //...
  23: }

Si vais a utilizar el atributo sobre vuestros métodos, condicionándolos a la existencia de constantes de compilación personalizadas, recordad que las constantes podéis definirlas:

  • desde las propiedades del proyecto en el IDE
  • en la línea de comandos del compilador (por ejemplo, /define:LOG)
  • variables de entorno del sistema operativo (set LOG=1)
  • en directivas sobre vuestro propio código (directiva #define LOG)

Eso sí, tened en cuenta que el método siempre será compilado e introducido en el ensamblado, son las invocaciones a éste las que son omitidas en caso de no existir la constante indicada.

Hay que tener en cuenta las siguientes observaciones para el uso del atributo Conditional:

  • Los métodos a los que se aplica no pueden tener tipo de retorno, es decir, serán void. Esto tiene bastante sentido, si pensamos en los efectos laterales que podría causar la desaparición de una invocación tras la cual se haga uso del valor retornado.
  • Sólo se pueden aplicar a métodos en clases o estructuras. Nada de interfaces (¡tampoco tendría mucho sentido!).
  • Si un método virtual está marcado como Conditional, las reescrituras de éste realizadas desde clases descendientes también lo estarán.  Es bastante lógico, puesto que así se mantienen las dependencias íntegras.
  • Si el atributo se aplica a un método, éste no puede ser un reemplazo del comportamiento de un antecesor (o sea, que no puede ser un override). También resulta muy lógico.

En resumen, se trata de un buen método para incluir código condicional en nuestros desarrollos, dependiente del contexto de compilación, evitando tener que usar directivas #if… #endif. Pero no lo olvidéis: cuidado con los efectos laterales citados.


Publicado en: Variable not found.