105 votes

Création d'une méthode générique en C#

Je suis en train d'essayer de combiner un tas de méthodes similaires en une méthode générique. J'ai plusieurs méthodes qui renvoient la valeur d'une chaîne de requête, ou null si cette chaîne de requête n'existe pas ou n'est pas dans le format correct. Ce serait assez facile si tous les types étaient nativement nullables, mais je dois utiliser le type générique nullable pour les entiers et les dates.

Voici ce que j'ai maintenant. Cependant, il renverra un 0 si une valeur numérique est invalide, et malheureusement, c'est une valeur valide dans mes scénarios. Quelqu'un peut-il m'aider? Merci!

public static T GetQueryString(string key) where T : IConvertible
{
    T result = default(T);

    if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
    {
        string value = HttpContext.Current.Request.QueryString[key];

        try
        {
            result = (T)Convert.ChangeType(value, typeof(T));  
        }
        catch
        {
            //Impossible de convertir. Renvoyer la valeur par défaut...
            result = default(T);
        }
    }

    return result;
}

78voto

Will Points 597

Et si vous spécifiez la valeur par défaut à retourner, au lieu d'utiliser default(T)?

public static T GetQueryString(string key, T defaultValue) {...}

Cela facilite également l'appel :

var intValue = GetQueryString("intParm", Int32.MinValue);
var strValue = GetQueryString("strParm", "");
var dtmValue = GetQueryString("dtmPatm", DateTime.Now); // par exemple utiliser la date d'aujourd'hui si non spécifiée

L'inconvénient est que vous avez besoin de valeurs magiques pour indiquer des valeurs de querystring invalides/manquantes.

17voto

Graviton Points 28358

Et ceci ? Changer le type de retour de T à Nullable

public static Nullable GetQueryString(string key) where T : struct, IConvertible
        {
            T result = default(T);

            if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
            {
                string value = HttpContext.Current.Request.QueryString[key];

                try
                {
                    result = (T)Convert.ChangeType(value, typeof(T));  
                }
                catch
                {
                    //Could not convert.  Pass back default value...
                    result = default(T);
                }
            }

            return result;
        }

17voto

Jay Points 27907

Je sais, je sais, mais...

public static bool TryGetQueryString(string key, out T queryString)

5voto

Sam Points 3346

Convert.ChangeType() ne gère pas correctement les types nullable ou les énumérations dans .NET 2.0 BCL (je pense que c'est corrigé pour BCL 4.0 cependant). Plutôt que de rendre l'implémentation externe plus complexe, faites en sorte que le convertisseur fasse plus de travail pour vous. Voici une implémentation que j'utilise :

public static class Converter
{
  public static T ConvertTo(object value)
  {
    return ConvertTo(value, default(T));
  }

  public static T ConvertTo(object value, T defaultValue)
  {
    if (value == DBNull.Value)
    {
      return defaultValue;
    }
    return (T) ChangeType(value, typeof(T));
  }

  public static object ChangeType(object value, Type conversionType)
  {
    if (conversionType == null)
    {
      throw new ArgumentNullException("conversionType");
    }

    // si ce n'est pas un type nullable, passez simplement les paramètres à Convert.ChangeType
    if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
    {
      // une valeur null entraîne une sortie nulle quel que soit le type de base
      if (value == null)
      {
        return null;
      }

      // c'est un type nullable, et non null, ce qui signifie qu'il peut être converti en son type sous-jacent,
      // donc écrasez le type de conversion passé avec ce type sous-jacent
      conversionType = Nullable.GetUnderlyingType(conversionType);
    }
    else if (conversionType.IsEnum)
    {
      // les chaînes nécessitent la méthode Parse
      if (value is string)
      {
        return Enum.Parse(conversionType, (string) value);          
      }
      // les types primitifs peuvent être instanciés en utilisant ToObject
      else if (value is int || value is uint || value is short || value is ushort || 
           value is byte || value is sbyte || value is long || value is ulong)
      {
        return Enum.ToObject(conversionType, value);
      }
      else
      {
        throw new ArgumentException(String.Format("La valeur ne peut pas être convertie en {0} - le type actuel n'est " +
                              "pas pris en charge pour les conversions d'énumération.", conversionType.FullName));
      }
    }

    return Convert.ChangeType(value, conversionType);
  }
}

Ensuite, votre implémentation de GetQueryString peut être :

public static T GetQueryString(string key)
{
    T result = default(T);
    string value = HttpContext.Current.Request.QueryString[key];

    if (!String.IsNullOrEmpty(value))
    {
        try
        {
            result = Converter.ConvertTo(value);  
        }
        catch
        {
            //Impossible de convertir. Renvoie la valeur par défaut...
            result = default(T);
        }
    }

    return result;
}

5voto

Epaphus Points 1011

Vous pouvez utiliser une sorte de monade Maybe (bien que je préfère la réponse de Jay)

public class Maybe
{
    private readonly T _value;

    public Maybe(T value)
    {
        _value = value;
        IsNothing = false;
    }

    public Maybe()
    {
        IsNothing = true;
    }

    public bool IsNothing { get; private set; }

    public T Value
    {
        get
        {
            if (IsNothing)
            {
                throw new InvalidOperationException("La valeur n'existe pas");
            }
            return _value;
        }
    }

    public override bool Equals(object other)
    {
        if (IsNothing)
        {
            return (other == null);
        }
        if (other == null)
        {
            return false;
        }
        return _value.Equals(other);
    }

    public override int GetHashCode()
    {
        if (IsNothing)
        {
            return 0;
        }
        return _value.GetHashCode();
    }

    public override string ToString()
    {
        if (IsNothing)
        {
            return "";
        }
        return _value.ToString();
    }

    public static implicit operator Maybe(T value)
    {
        return new Maybe(value);
    }

    public static explicit operator T(Maybe value)
    {
        return value.Value;
    }
}

Votre méthode ressemblerait à ceci :

    public static Maybe GetQueryString(string key) where T : IConvertible
    {
        if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
        {
            string value = HttpContext.Current.Request.QueryString[key];

            try
            {
                return (T)Convert.ChangeType(value, typeof(T));
            }
            catch
            {
                // Impossible de convertir. Renvoyer la valeur par défaut...
                return new Maybe();
            }
        }

        return new Maybe();
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X