C#: Conversiones (explícitas o implícitas) e interfaces

Una de las características más útiles, aunque más potencialmente peligrosas de C# es la posibilidad de sobrecargar los operadores de conversión (casting) y concretamente el de conversión implícita.

Poder sobrecargar el operador de conversión explícita, aunque lo entiendo como una característica que agrega ortogonalidad al lenguaje, no es algo que me guste. Antes de eso prefiero crear un método AsXXX(). De hecho me parece que un cliente de mi clase encontrará más lógico un método AsXXX() que no “un casting a XXX” que debes saber que se puede hacer para hacerlo.

Continúa leyendo C#: Conversiones (explícitas o implícitas) e interfaces

Crear imágenes Docker de proyectos netcore en varias versiones del framework

Imagina que estás probando alguna versión release de netcore (pongamos la 2.2-preview3) y quieres generar imágenes Docker de tu proyecto para esa imagen. Pero a la vez quieres también crear las imágenes usando la última versión estable (pongamos la 2.1).

Asumiendo que el código fuente es compatible, ¿como puedes gestionar eso sin morir en el intento?

Continúa leyendo Crear imágenes Docker de proyectos netcore en varias versiones del framework

C#: Structs de un solo campo como typedefs

No hace mucho me preguntaba si usar structs de un solo campo tenía alguna penalización respecto a usar, simplemente, una variable del tipo del campo. Es decir, me preguntaba si tener:

struct Sint {
   public int value;
}

Tenía alguna penalización al respecto de usar, simplemente, una variable int.

A nivel de memoria sospechaba que no: una struct ocupa lo mismo que la suma de todos sus campos, más los paddings que se agregan para que los campos estén alineados, más el padding final que se agrega para que, en el caso de un array, los elementos estén alineados. En el caso de una estructura que solo tenga un int esos paddings no existen (*):

namespace PaddingTest
{
    struct NoPadding
    {
        public int i1;
    }

    struct Padding
    {
        public bool b1;
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"{sizeof(int)} vs {Marshal.SizeOf<NoPadding>()}");
            Console.WriteLine($"{sizeof(bool)} vs {Marshal.SizeOf<Padding>()}");
            Console.ReadLine();
        }
    }
}

Si ejecutas este código, lo más habitual es que veas la primera línea “4 vs 4” y la segunda línea “1 vs 4”. Eso es porque, a pesar de que un bool ocupa un solo byte, una estructura que contenga un bool ocupa 4 bytes, debido al padding para alinear la memoria. Esos alineamientos son importantes, ya que no tener los datos alineados supone una pérdida de rendimiento importante (el procesador debe efectuar operaciones adicionales).

(*) Cuando digo que usando un int esos paddings no existen me estoy refiriendo a mi arquitectura (x64). Eso es importante. Observa que no uso sizeof para medir el tamaño de una struct. Eso es porque no se puede. Las structs no tienen un tamaño predefinido (a diferencia de los tipos propios como Int32 que siempre son 32 bits). Eso es debido, precisamente a ese padding: el CLR puede decidir de empaquetar una estrucutra con distintos paddings en función de la arquitectura sobre la cual se ejecuta el programa y por ello, al no ser el tamaño constante, no podemos usar sizeof.

La otra pregunta era si el rendimiento afectaba. Encontré este post donde parece ser que demuestra que no. De todos modos el test que se hace en este post se limita a comprobar si acceder a un campo de la estructura penaliza. Pero yo quiero más que eso: yo quiero ver si recibir, crear y devolver la estructura tiene impacto en el rendimiento. En definitiva quiero comprobar la diferencia entre esas dos funciones:

static int IntOp(int i)
{
    var j = i + 10;
    return j;
}
static Sint SintOp(Sint i)
{
    var j = new Sint(i.Value + 10);
    return j;
}

La primera opera solo con ints y la segunda solo con mi estructura (que solo tiene un int). Así que elaboré un test que ejectuaba esas funciones 100 millones de veces:

struct Sint
{
    public readonly int Value;
    public Sint(int v) => Value = v;
}
class Program
{
    static void Main(string[] args)
    {
        const int MAX_LOOP= 100000000;
        const int MAX_REPS = 10;

        long[] intTimes = new long[MAX_REPS];
        long[] sintTimes = new long[MAX_REPS];

        for (var rep = 0; rep<MAX_REPS; rep++)
        {
            var (tint, tsint) = Test(MAX_LOOP);
            intTimes[rep] = tint;
            sintTimes[rep] = tsint;
        }

        for (var idx=0; idx < MAX_REPS; idx++)
        {
            var diff = sintTimes[idx] - intTimes[idx];
            var percent = ((double)diff / (double)intTimes[idx]) * 100;
            Console.WriteLine($"int took {intTimes[idx]}. Sint took {sintTimes[idx]}. Diff is {diff} ({percent}%)");
        }
        Console.ReadLine();
    }
    private static (long, long) Test(int times)
    {
        var s = new Stopwatch();
        s.Start();
        for (var idx = 0; idx < times; idx++)
        {
            var r = IntOp(idx);
        }
        s.Stop();
        var ms1 = s.ElapsedMilliseconds;
        s.Reset();
        s.Start();
        for (var idx = 0; idx < times; idx++)
        {
            var r = SintOp(new Sint(idx));
        }
        s.Stop();
        var ms2 = s.ElapsedMilliseconds;
        s.Reset();
        return (ms1, ms2);
    }
    static int IntOp(int i)
    {
        var j = i + 10;
        return j;
    }

    static Sint SintOp(Sint i)
    {
        var j = new Sint(i.Value + 10);
        return j;
    }
}

El programa repite el test 5 veces y al final muestra los resultados. Lo ejecuté varias veces, pero el patrón es siempre muy similar. En mi máquina, compilado en Release esos fueron los resultados:

int took 254. Sint took 569. Diff is 315 (124.015748031496%)
int took 244. Sint took 447. Diff is 203 (83.1967213114754%)
int took 249. Sint took 436. Diff is 187 (75.1004016064257%)
int took 236. Sint took 460. Diff is 224 (94.9152542372881%)
int took 235. Sint took 441. Diff is 206 (87.6595744680851%)
int took 251. Sint took 427. Diff is 176 (70.1195219123506%)
int took 241. Sint took 446. Diff is 205 (85.0622406639004%)
int took 241. Sint took 452. Diff is 211 (87.551867219917%)
int took 263. Sint took 472. Diff is 209 (79.467680608365%)
int took 258. Sint took 473. Diff is 215 (83.3333333333333%)

Es decir hay una pérdida de rendimiento que oscila entre el 70 y el 100% aproximadamente. Ahora ya se trata de juzgar de si es asumible.

En mi caso decidí que sí, a pesar de la pérdida de rendimiento, tampoco iba a tener un número de operaciones tan bestia (observad que estamos hablando de alrededor de perder un cuarto de segundo cada 100 millones de operaciones).

¿Y por qué hacer esto?

Esa es una buena pregunta, claro. O sea, que ventajas tiene usar una estructura de un solo campo respecto a usar una variable. Es decir, si ya tengo ints, para ¿qué narices quiero usar la estructura Sint?

Una  respuesta es para dar semántica al código. Te pongo mi ejemplo concreto. Tengo una función al que se le pasa un color. Hay 8 colores predefinidos, con valores de 0 a 7, pero luego se pueden definir colores propios a los que se les asignan índices de 8 para arriba (el número de colores que se pueden definir depende de la plataforma de ejecución). Todas las funciones de pintado esperan el color que como puedes ver es un int (cuyo rango va de 0 hasta un valor máximo que no se conoce a priori).

Usar una estructura Color con un solo campo int, me permite definir una semántica propia: las funciones que reciben/devuelven un Color sé que trabajan con colores. En definitiva estoy tipando el parámetro o valor de retorno. Otra opción es que puedo usar métodos de extensión (o en la propia estructura) para agrupar operaciones que se pueden hacer con colores. Y además todas aquellas operaciones que se pueden hacer con ints y no con colores (como dividirlos p. ej.) no se pueden realizar.

En mi caso, por si te pica la curiosidad, terminé con esa estructura:

public static class TvColorNames
{
    public const int Black = 0;
    public const int Red = 1;
    public const int Green = 2;
    public const int Yellow = 3;
    public const int Blue = 4;
    public const int Magenta = 5;
    public const int Cyan = 6;
    public const int White = 7;
    public static IEnumerable<int> AllStandardColors() => new[] { Black, Red, Green, Yellow, Blue, Magenta, Cyan, White };
    public const int StandardColorsCount = 8;
}

public struct TvColor
{
    public readonly int Value;
    public TvColor(int value) => Value = value;
    public TvColor Plus(int valueToAdd) => new TvColor(Value + valueToAdd);

    public readonly static TvColor Black = new TvColor(TvColorNames.Black);
    public readonly static TvColor Red = new TvColor(TvColorNames.Red);
    public readonly static TvColor Green = new TvColor(TvColorNames.Green);
    public readonly static TvColor Yellow = new TvColor(TvColorNames.Yellow);
    public readonly static TvColor Blue = new TvColor(TvColorNames.Blue);
    public readonly static TvColor Magenta = new TvColor(TvColorNames.Magenta);
    public readonly static TvColor Cyan = new TvColor(TvColorNames.Cyan);
    public readonly static TvColor White = new TvColor(TvColorNames.White);

    public static explicit operator short(TvColor color) => (short)color.Value;
    public static bool operator ==(TvColor one, TvColor other) => one.Value == other.Value;
    public static bool operator !=(TvColor one, TvColor other) => one.Value != other.Value;

    public override bool Equals(object obj)
    {
        if (obj is TvColor) return ((TvColor)obj).Value == Value;
        if (obj is int) return (int)obj == Value;
        return base.Equals(obj);
    }
    public override int GetHashCode() => Value.GetHashCode();
}

Y el código queda un poco más legible, ya que las funciones que operan con colores se definen en base a este tipo, no en base a un int como p. ej:

public interface IStyleBuilder
{
    IStyleBuilder DesiredStandard(TvColor fore, TvColor back,CharacterAttributeModifiers attributes = CharacterAttributeModifiers.Normal);
    IStyleBuilder DesiredFocused(TvColor fore, TvColor back, CharacterAttributeModifiers attributes = CharacterAttributeModifiers.Normal);
}

En general uso esta estructura para el mismo caso en que, si los valores fueran fijos, usaría un enum. Si solo hubiese la posibilidad de usar los 8 colores, hubiese creado un enum con esos 8 valores. Pero, al haber la posibilidad de crear más colores y de no saber a priori cuantos son, me impide usar un enum y por ello uso la estructura. Pero en ambos casos la idea es la misma: dotar el código de semántica, hacerlo más claro.

Que valga la pena o no pagar el precio de rendimiento eso ya queda a criterio de cada cual.

Depurar netcore ejecutándose en WSL con VS2017

Hoy he tenido que afrontar la dolorosa situación de un código en netcore que funcionaba correctamente en mi máquina (Windows 10) pero que al ejecutarse en Docker fallaba miserablemente.

Dado que el error que imprimía por consola era que no encontraba un fichero, lo primero fue comprobar que nuestro código era cross-platform (p. ej. que usábamos siempre Path.Combine y no concatenábamos el caráter \ para separar directorios) ya que la imagen Docker era de Linux. Luego hicimos todas las verificaciones habidas y por haber (permisos, etc) y nada. Todo parecía correcto. Al final, nos preguntamos si era un problema de Docker o de cualquier Linux en general y ahí acudimos a WSL.

Ejecutamos el programa bajo WSL y obtuvimos el mismo error. Eso era bueno por dos motivos: No era Docker la fuente del error y además nos permitía depurar el proceso.

Continúa leyendo Depurar netcore ejecutándose en WSL con VS2017

Crear imágenes Docker de una app SPA pura y… como configurarla

El otro día habé de como crear imágenes Docker para las aplicaciones SPA de .NET Core. Hoy quiero comentaros como crear imágenes Docker para aplicaciones SPA puras y un tema importante al respecto: como configurarlas.

Continúa leyendo Crear imágenes Docker de una app SPA pura y… como configurarla

Generar imágenes Docker de proyectos SPA de netcore

¡Buenas!

Cuando creas un proyecto SPA de netcore, ya sea mediante VS o bien usando dotnet new y alguna plantilla SPA como react (dotnet new react), se genera una estructura parecida a la siguiente:

Estructura ficheros proyecto SPA

La carpeta “ClientApp” contiene todo el código de cliente (javascript, CSS y demás) mientras que el resto es el código netcore que se limita a “lanzar” la SPA.

Continúa leyendo Generar imágenes Docker de proyectos SPA de netcore

Unicode y encodings

Uno de los conceptos que hoy en día siguen causando más confusión es el de Unicode y sus distintos tipos de codificación. Pero… ¿qué es realmente Unicode? Para ello, déjame que remonte unos cuantos años atrás…

El inicio: ASCII

Los ordenadores los inventaron los americanos y como suele ocurrir se preocuparon de lo suyo: que un ordenador pudiese presentar textos en su idioma. Tampoco hay tantos caracteres en el inglés: las veinte y poco letras (en mayúsculas y minúsculas), los símbolos de puntuación, paréntesis, operaciones matemáticas y poco más. En total eran menos de 128 carácteres. Genial, porque esos espacios que sobraban se podían aprovechar para colocar otros caracteres que no tienen representación gráfica pero que eran (y son) necesarios para controlar el teletipo: retornos de carro, saltos de línea y similares. Había nacido el código ASCII.

Continúa leyendo Unicode y encodings

Ejecutar bases de datos en Kubernetes: ¿Sí, no?

Esta es una de las preguntas que tarde o temprano cualquiera que trabaje con Docker y Kubernetes (o cualquier otro orquestrador) se termina encontrando: Tienes toda tu aplicación en contenedores, ejecutándose en Kubernetes. Sabes también que existen versiones dockerizadas de las bases de datos. Así pues… ¿es conveniente ejecutar las bases de datos como contenedores adicionales en el orquestrador?

Continúa leyendo Ejecutar bases de datos en Kubernetes: ¿Sí, no?

Azure Functions y SignalR: serverless push

El hecho de ofrecer SignalR como servicio PaaS en Azure y su integración con Azure Functions nos abre un escenario interesante: ahora es facilísimo hacer notificaciones push desde una Azure Function (AF) a un cliente SignalR (p. ej. una Web).

Continúa leyendo Azure Functions y SignalR: serverless push