Portada » Preguntas Frecuentes de .NET 6 » Lista de extension methods que pueden serte de utilidad

Lista de extension methods que pueden serte de utilidad

En este artículo quiero dejar múltiples extension methods que he ido encontrando o aplicando en mi código y me han sido útiles, así como un ejemplo de uso.

La idea de tenerlos así y no directamente en un repositorio o librería es que en múltiples ocasiones no vas a necesitar todas estas extensiones, por lo que lo mejor es aplicar YAGNI y sólo copiar este código cuando de verdad lo vayas a utilizar.

Esta lista está en constante crecimiento.

Tabla de contenidos

ValueOrDefault

public static int ValueOrDefault(this string @this, int defaultVal) 
    => string.IsNullOrWhiteSpace(@this) || !int.TryParse(@this, out var parsedValue) ?        
           defaultVal : parsedValue;

public static string ValueOrDefault(this string @this, string defaultVal) 
    => string.IsNullOrWhiteSpace(@this) ? defaultVal : @this;

Ejemplo de uso:

Imaginemos que tenemos un archivo de configuración (el appsettings.json mismamente) donde almacenamos determinadas FeatureFlags o configuraciones globales. Necesitamos estas configuraciones para luego utilizarlas más adelante en el pipeline o algún middleware.

En el caso que se muestra en el ejemplo, nuestra clase «Settings» tiene solo 4 propiedades, pero podrían perfectamente ir aumentando con el tiempo.

Este sería el código original:

Settings settings = new Settings();

string nor = builder.Configuration.GetSection("NumberOfRetries").Value;
var norHasValue = int.TryParse(nor, out var norValue);
if (norHasValue)
    settings.NumberOfRetries = norValue;
else
    settings.NumberOfRetries = 5;

string htsp = builder.Configuration.GetSection("HourToStartPollingAt").Value;
var htspHasValue = int.TryParse(htsp, out var htspValue);
if (htspHasValue)
    settings.HourToStartPollingAt = htspValue;
else
    settings.HourToStartPollingAt = 0;

string aea = builder.Configuration.GetSection("AlertEmailAddress").Value;
if (string.IsNullOrWhiteSpace(aea))
    settings.AlertEmailAddress = "xyz@example.com";
else
    settings.AlertEmailAddress = aea;

string sn = builder.Configuration.GetSection("ServerName").Value;
if (string.IsNullOrWhiteSpace(sn))
    settings.ServerName = "TestServer";
else
    settings.ServerName = sn;

Ahora aplicamos los nuevos extension methods y sustituimos:

Settings settings = new Settings()
{
    NumberOfRetries = builder.Configuration.GetSection("NumberOfRetries").Value.ValueOrDefault(5),
    HourToStartPollingAt = builder.Configuration.GetSection("HourToStartPollingAt").Value.ValueOrDefault(0),
    AlertEmailAddress = builder.Configuration.GetSection("AlertEmailAddress").Value.ValueOrDefault("xyz@example.com"),
    ServerName = builder.Configuration.GetSection("ServerName").Value.ValueOrDefault("TestServer")
};

De esta forma es sustancialmente más sencillo volver y realizar cambios o sencillamente añadir nuevas configuraciones en la clase Settings.

DoIfNotNull

public static T2 DoIfNotNull<T1, T2>(this T1 @this, Func<T1, T2> func) 
    => !EqualityComparer<T1, T2>.Default.Equals(@this, default) ? func(@this) : default;

Ejemplo de uso:

Realmente esta extensión es bastante genérica y se aplica a múltiples situaciones donde hacemos una verificación de nulos previos a intentar acceder a un método dentro de una clase.

static string PrependHello(string x) => $"Hello {x}!";

var person1 = GetPerson("Victor Pérez Asuaje"); // Devuelve un valor
var person2 = GetPerson("Persona NoExiste"); // Devuelve nulo

var hello1 = "";
if(person1 != null)
    hello1 = PrependHello(person1.Name); // Devuelve "Hello Victor Pérez Asuaje!"

var hello2 = "";
if(person2 != null)
    hello2 = PrependHello(person2.Name); // No entra

Si aplicamos el nuevo extension method:

static string PrependHello(string x) => $"Hello {x}!";

var person1 = GetPerson("Victor Pérez Asuaje");
var person2 = GetPerson("Persona NoExiste");

var hello1 = person1.DoIfNotNull(x => PrependHello(x.Name));
var hello2 = person2.DoIfNotNull(x => PrependHello(x.Name));

Validate<T>

public static bool Validate<T>(this T @this, params Func<T, bool>[] predicates)
    => predicates.All(x => x(@this));

Ejemplo de uso:

Cada vez que queremos hacer validaciones masivas de una propiedad, de manera que si cualquiera fallase, no puede continuar. Un ejemplo simple es la validación de una contraseña.

public static bool ValidatePassword(string password)
{
    if (password == null) return false;

    if(password.Length < 8 || password.Length > 18) return false;

    if (!password.Any(char.IsUpper)) return false;

    if(!password.Any(char.IsLower)) return false;

    if(!password.Any(char.IsNumber)) return false;

    List<char> allowedChars = new List<char>() { '@', '!', '#', '$', '*' };

    if (!password.Any(c => allowedChars.Contains(c))) return false;

    return true;
}

Aplicando el nuevo extension method, lo que hacemos es validar que se cumplan todos los requisitos indicados.

public static bool ValidatePassword(string password) 
    => password.Validate(
        x => x != null,
        x => x.Length >= 8 && x.Length <= 18,
        x => x.Any(char.IsUpper),
        x => x.Any(char.IsLower),
        x => x.Any(char.IsNumber),
        x => x.Any(c => new List<char> { '@', '!', '#', '$', '*' }.Contains(c))
    );


Referencias:

  1. Hacking C#: Development for the Truly Lazy – Simon Painter – NDC Copenhagen 2022