comment utiliser.TryParse dans une méthode générique alors que T est soit double ou Int

dans un de mes projets, j'utilise deux méthodes. 1. GetDoubleValue et 2. GetIntValue. Getdoulevalue utilise le double.TryParse de paramètre str chaîne de caractères et renvoie 0 si elle ne parvient pas tout GetIntValue tente int.TryParse de paramètre str chaîne de caractères et renvoie 0 en cas d'échec. Ce que je veux, c'est combiner ces deux méthodes en une seule méthode générique qui, avec string str, reçoit aussi le paramètre T, de sorte que si je veux utiliser la méthode GetDoubleValue, je peux utiliser le double pour le paramètre T et si je veux pour utiliser la méthode GetIntValue je peux utiliser Int pour le paramètre t

public double GetDoubleValue(string str)
{
    double d;
    double.TryParse(str, out d);
    return d;
}
public int GetIntValue(string str)
{
    int i;
    int.TryParse(str, out i);
    return i;
}

Note: j'ai essayé quelque chose comme ça:

private T GetDoubleOrIntValue<T>(string str) where T : struct 
{
    T t;
    t.TryParse(str, out t);
    return t;
}

EDIT

dans ma base de données, j'ai plus de 30 colonnes dans des tables de différences ayant des types de données numériques. Je veux insérer 0 dans chaque colonne si l'utilisateur ne tape rien dans l'encadré I. il laisse la totalité ou une partie des boîtes de texte vides. Si je n'utilise pas la méthode GetIntValue je vais devoir utiliser le corps de la méthode plus de 30 temps. c'est pourquoi je le fais par l'approche méthodique. J'écris trois exemples sur plus de trente par exemple

cmd.Parameters.Add("@AddmissionFee", SqlDbType.Decimal).Value = GetIntValue(tbadmissionfee.Text);
cmd.Parameters.Add("@ComputerFee", SqlDbType.Decimal).Value = GetIntValue(tbcomputerfee.Text);
cmd.Parameters.Add("@NotesCharges", SqlDbType.Decimal).Value = GetDoubleValue(tbnotescharges.Text);

je veux combiner les deux méthodes susmentionnées parce qu'aujourd'hui j'ai deux méthodes comme celle-ci qui, si elles sont combinées, ne donneront pas de meilleure amélioration dans la programmation, mais demain j'aurai peut-être des dizaines de méthodes comme celle-ci qui seront mieux combinées en une seule méthode générique. E. g I may have GetInt32Value,GetShortValue etc. j'espère qu'il est maintenant clair pourquoi je veux cette???

18
demandé sur kashif 2012-05-13 23:19:31

8 réponses

Vous pourriez faire quelque chose comme ceci:

   public static T GetDoubleOrIntValue<T>(this string str) where T : IConvertible
{
    var thisType = default(T);
    var typeCode = thisType.GetTypeCode();
    if (typeCode == TypeCode.Double)
    {
        double d;
        double.TryParse(str, out d);
        return (T)Convert.ChangeType(d,typeCode) ;
    }
    else if (typeCode == TypeCode.Int32)
    {
        int i;
        int.TryParse(str, out i);
        return (T)Convert.ChangeType(i, typeCode);
    }
    return thisType;
}

Puis, quand vous l'appelez:

string d = "1.1";
string i = "3";

double doubleValue = d.GetDoubleOrIntValue<double>();
int intValue = i.GetDoubleOrIntValue<int>();

EDIT: J'ai vu quelqu'un d'autre utiliser Convert.ChangeType...qui prévoit un type de retour Générique.

5
répondu Eric Dahlvang 2012-05-13 21:14:23

je suis d'accord avec Mark Byers. Ce n'est probablement pas une bonne idée d'essayer de faire cette méthode générique. Un peu de duplication de code ne fera pas de mal (tant que ce n'est vraiment qu'un peu). Le fait que vous pourriez utiliser tout struct avec votre version générique aussi ne fait pas passer ça pour une bonne idée pour moi.

si vous voulez vraiment faire cela, vous pouvez essayer d'utiliser la réflexion (comme suggéré par la MINUSTAH), mais ce serait à la fois laid et lent.

à la place, vous pourriez utiliser Convert.ChangeType():

private T GetValue<T>(string str) where T : struct 
{
    return (T)Convert.ChangeType(str, typeof(T));
}
10
répondu svick 2012-05-13 19:40:57

je voudrais envisager la rédaction d'une extension ou une méthode statique:

delegate bool TryParse<T>(string str, out T value);

public static T GetValue<T>(this string str, TryParse<T> parseFunc)
{
    T val;
    parseFunc(str, out val);
    return val;
}

vous devez alors fournir le TryParse mise en œuvre que vous souhaitez utiliser:

int i = "1234".GetValue<int>(int.TryParse);

Être averti que cette fonction silencieusement retourne une valeur par défaut si l'analyse échoue. Vous pouvez retourner default(T) si le TryParse delegate retourne false.

8
répondu Lee 2012-05-13 19:41:38

Donc au lieu d'écrire:

double d = GetDoubleValue(str);

vous voulez être capable d'écrire ceci?

double d = GetValue<double>(str);

même si vous pouvez le faire fonctionner, Quel est l'avantage? Personnellement, je ne pense pas que ce soit une grande amélioration pour le client. Le seul avantage est de ne pas avoir à appliquer la même méthode deux fois. Mais étant donné la simplicité de la méthode et la difficulté de mettre en œuvre ce type de réutilisation du code, il semble raisonnable ici de dupliquer ces quelques lignes de code.

vous n'êtes pas seul avec ce problème. Jetez un coup d'oeil à d'autres méthodes dans le cadre .NET qui fonctionnent sur différents types et voir comment ils l'ont résolu. Voici comment BinaryReader permet de lire différents types:

ce n'est pas joli, mais c'est comme ça que ça se passe.


en ce qui concerne votre mise à jour, j'ai deux autres remarques à faire.

Vous mentionner que vous il y a probablement plus de deux types, ce qui pourrait entraîner une plus grande duplication du même code. Étant donné que la méthode est très concise et simple, il ne semble pas un problème de copier et coller le code. C'est une exception raisonnable à la règle que vous ne devriez pas copier et coller du code, à mon avis. L'approche ici réduit la quantité de duplication, bien que vous ayez encore besoin de beaucoup de méthodes nommées de la même façon dans votre interface publique.

cependant je pense qu'il est important de mentionnez qu'il semble erroné d'utiliser silencieusement une valeur par défaut dans votre situation, comme Lee l'a déjà mentionné dans un commentaire. Si un utilisateur fait une erreur lors de la saisie des données, il doit recevoir un message d'erreur. Vous devriez utiliser une sorte de cadre de validation pour vous assurer que les chaînes sont valides et pour informer votre utilisateur du problème s'il n'est pas valide. Une fois que vous avez validé tout, il est sûr à utiliser int.Parse au lieu de int.TryParse parce que vous savez que le traitement réussisse. Et si l'analyse échoue, c'est une situation exceptionnelle (un bug dans votre validation) donc il semble juste que l'application se dérobe avec une exception et enregistre une trace de pile. Cela vous aidera à trouver et à corriger le bogue.

6
répondu Mark Byers 2012-05-14 17:54:56

j'ai fait un simple échantillon. Le code n'est pas optimal, mais il fonctionne comme prévu.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GetValue<int>("123"));
        Console.WriteLine(GetValue<double>("123.123"));
        Console.WriteLine(GetValue<DateTime>("2001-01-01 01:01:01"));
    }

    static T GetValue<T>(string s)
    {
        var tryParse = typeof (T).GetMethod(
            "TryParse", new [] {typeof(string), typeof(T).MakeByRefType()});
        if (tryParse == null)
            throw new InvalidOperationException();


        T t = default (T);
        var parameters = new object[] {s, t};
        var success = tryParse.Invoke(null, parameters);
        if ((bool) success) t = (T)parameters[1];
        return t;
    }
}
2
répondu ie. 2012-05-13 19:58:26

Vous pouvez essayer avec la réflexion: d'abord, essayer de trouver une méthode "TryParse" sur le type remplacé par .

T t;
var typeInfo = typeof(T);
var method = typeInfo.GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static);
var result = (bool)method.Invoke(null, new object[] { str, t });

if (result)
    return t;

ce code n'est pas testé; il n'est pas non plus optimal du point de vue des performances.

1
répondu Minustar 2012-05-13 19:31:19

Comme vous le voyez, vous ne pouvez pas restreindre where TDouble ou Int32, c'est pourquoi vous avez probablement struct sur cette place (int et double ne sont pas struct). Les types de valeurs ne peuvent pas être utilisés comme restrictions sur les génériques.

donc vous ne pouvez pas taper safe assurez-vous que TryParse la méthode existe. Vous devez soit taper T et jeter une exception si elle est soit double ou int comme dans la réponse par @ie.

Ma suggestion est d'utiliser différents signature:

private static bool GetValue(string str, out double result)
{
...    
}

private static bool GetValue(string str, out int result)
{
...    
}

alternativement vous pouvez introduire une interface IParsable. Boîte double ou int résultat et implémentation de la conversion implicite en double ou entier.

0
répondu George Mamaladze 2012-05-13 19:40:41

Qu'une méthode d'extension ?

public static class Extensions
{    
    public static Nullable<T> TryParse<T>(this String str) where T:struct
    {
        try
        {
            T parsedValue = (T)Convert.ChangeType(str, typeof(T));
            return parsedValue;
        }
        catch { return null; }
    }
}

utilisation :

int i = "123".TryParse<int>() ?? 0;
0
répondu Fboisde 2016-05-05 12:20:16