Share via


#retosMSDN: Solución al Reto 5 – Extendiendo funcionalidad en C#

Aquí tienes la solución que proponemos para los 4 mini-retos que componen el quinto de nuestros #retosMSDN: Reto 5 – Extendiendo funcionalidad en C#. ¡Muchas gracias una semana más a todos los que habéis participado en el reto!

 

Nuestra solución

  

1) Duration y su método From:

 

 public enum Duration { Day, Week, Month };

public static class EnumExtensions
{
    public static DateTime From(this Duration duration, DateTime dateTime)
    {
        switch (duration)
        {
            case Duration.Day:
                return dateTime.AddDays(1);
            case Duration.Week:
                return dateTime.AddDays(7);
            case Duration.Month:
                return dateTime.AddMonths(1);
            default:
                throw new ArgumentOutOfRangeException("duration");
        }
    }
}
  

Duration es un enum al que le hemos añadido funcionalidad mediante un método de extensión.

 

2) El método NotNull:

 

 public static class ClassExtensions
{
    public static T NotNull<T>(this T @this, string paramName = null, [CallerMemberName] string callerName = null)
        where T : class
    {
        if (@this == null)
        {
            throw new ArgumentNullException(paramName, string.Format("{0} was called with null parameter", callerName));
        }

        return @this;
    }
}

 

NotNull es un método de extensión al que le hemos puesto la restricción con where de que el tipo con el que lo utilicemos sea una clase sí o sí, y no un tipo básico (p.ej. int), o un enum, o cualquier otra cosa. Además, con el atributo CallerMemberName podemos obtener el nombre del método que llamó a NotNull. 

 

3) El diccionario DictionaryPlus:

 

 public class DictionaryPlus<TKey, TValue> : Dictionary<TKey, TValue>
{
    public IEnumerable<TValue> this[params TKey[] keys]
    {
        get
        {
            return keys.Select(key => base[key]);
        }
    }
}

 

DictionaryPlus no es más que un Dictionary al que le hemos añadido un nuevo indexer al que le podemos pasar un número variable de parámetros gracias a params. Después el array de parámetros recibido lo manipulamos con Linq. Al no poder añadir indexers como métodos de extensión de la clase Dictionary, no nos ha quedado más remedio que heredar de dicha clase.

 

4) El método ToUpperNoCopy:

 

 public static class StringExtensions
{
    public static unsafe void ToUpperNoCopy(this string @this)
    {
        if (@this == null)
        {
            throw new ArgumentNullException("@this");
        }

        fixed (char* chars = @this)
        {
            for (char* @char = chars; *@char != 0; @char++)
            {
                *@char = char.ToUpper(*@char);
            }
        }
    }
}

 

Los strings en C# son inmutables, es decir, que su contenido no puede ser cambiado después de que el objeto sea creado. A no ser que manipules directamente su contenido en memoria con punteros. Para poder usar punteros tenemos que marcar el método de extensión que hemos creado como unsafe, para lo que también tenemos que marcar nuestra librería para que sea compilada como unsafe. Tenemos que tener cuidado para que el Garbage Collector no nos mueva de sitio la memoria que estamos manipulando a mitad de la operación, cosa que podemos prevenir con fixed.

 

El código completo lo puedes encontrar en esta solución de Visual Studio 2013 que puedes descargarte de GitHub.

 

Vuestras soluciones

 

Como siempre, no hay una única manera de resolver los retos.

Por ejemplo, @lantoli ha implementado Duration como una clase en lugar de un enum:

 

     public class Duration
    {

        private readonly Func<DateTime, DateTime> func;

        private Duration(Func<DateTime, DateTime> func = null) {       
             this.func = func;
        }

        public DateTime From(DateTime date) {
            if (func == null) 
                throw new ArgumentOutOfRangeException();
            return func(date);
        }


        public static explicit operator Duration(int num) {
            return new Duration();
        }


        public static Duration Day {
            get {
                return new Duration(t => t.AddDays(1));
            }
        }

        public static Duration Week {
          get {
                return new Duration(t => t.AddDays(7));
          }
        }

        public static Duration Month {
            get {
                return new Duration(t => t.AddMonths(1));
            }
        }
    }

 

Es muy interesante en esta solución el uso que hace de las expresiones lambda así como del operador explícito para poder convertir un entero cualquiera en un objeto de tipo Duration.

 

Por otro lado, @rsciriano ha optado por no utilizar Linq en DictionaryPlus, y usar yield para devolver el enumerable que pedíamos:

 

     public class DictionaryPlus<TKey, TValue> : Dictionary<TKey, TValue>
    {
        public IEnumerable<TValue> this[params TKey[] keys] 
        { 
            get
            {
                foreach (var item in keys)
                {
                    yield return base[item];
                }
            }
        }
    }

 

 

Las entradas

 

¡Las 2 entradas del Codemotion 2014 son para @angel_g_santos y @rsciriano! ¡Enhorabuena y nos vemos allí!

 

¡El próximo viernes 14 de noviembre publicaremos el siguiente de nuestros #retosMSDN! Y si quieres retar al resto de la comunidad con tu propio reto, recuerda que puedes enviárnoslo a esmsdn@microsoft.com.

Un saludo,

Alejandro Campos Magencio (@alejacma)

Technical Evangelist

PD: Mantente informado de todas las novedades de Microsoft para los desarrolladores españoles a través del Twitter de MSDN, el Facebook de MSDN, el Blog de MSDN y la Newsletter MSDN Flash.