2012-05-13 9 views
17

in uno dei miei progetti che sto utilizzando seguendo due metodi. 1. GetDoubleValue e 2. GetIntValue. GetDoubleValue utilizza double.TryParse per il parametro str string e restituisce 0 se fallisce mentre GetIntValue prova int.TryParse per il parametro str string e restituisce 0 se fallisce. Quello che voglio è combinare questi due metodi in un metodo generico che insieme alla stringa str riceva anche il parametro T così che se voglio usare il metodo GetDoubleValue posso usare double per il parametro T e se voglio usare il metodo GetIntValue posso usare Int per il parametro Tcome utilizzare <T>. TryParse in un metodo generico mentre T è o doppio o Int

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; 
} 

Nota: ho provato qualcosa di simile;

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

EDIT

nel mio database Ho più di 30 colonne in tabelle differenct hanno tipo di dati numerici. Voglio inserire 0 in ogni colonna se l'utente non scrive nulla nella casella di testo in cui lascia tutte o alcune delle caselle di testo vuote. Se non utilizzo il metodo GetIntValue, dovrò utilizzare il metodo body più di 30 volte. è per questo che lo sto facendo attraverso l'approccio metodologico. Sto scrivendo tre dei più di trenta esemplari per esempio

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); 

voglio combinare i suddetti due metodi perché oggi sto avendo due metodi come questo che, se combinati non si ottiene alcun miglioramento meglio nella programmazione, ma domani potrei avere decine di metodi come questo che saranno meglio combinati in un unico metodo generico. ad esempio, potrei avere GetInt32Value, GetShortValue ecc. Spero che ora sia cancellato perché voglio questo ???

+2

Con il raccolto di vincoli di tipo generico, questo non è possibile. – Oded

+0

Come chiamerai comunque questo metodo generico? – BoltClock

+2

Sei sicuro di volere che questa funzione restituisca automaticamente un valore predefinito se l'analisi fallisce? – Lee

risposta

5

Si potrebbe fare qualcosa di simile:

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; 
} 

Poi, quando si chiama:

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

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

ma il tutto sembra un pò sciocco a me.

MODIFICA: ha visto qualcun altro utilizzando Convert.ChangeType ... che fornisce un tipo di ritorno generico.

+0

Grazie Eric Dahlvang. il codice che hai fornito è abbastanza grande da essere il sostituto dei miei due metodi sopra indicati, come suggerito da Mark Byers – kashif

+0

Ho riflettuto sulla tua codifica oggi. non è davvero una cattiva soluzione per il mio problema. molte grazie. – kashif

+0

devo dire che questo fa davvero schifo, tanto quanto amo C# voglio avere un convert.try (tipo int, valore dell'oggetto) – CMS

1

Si potrebbe provare con la riflessione: in primo luogo, provare a trovare un metodo "TryParse" sul tipo sostituito da <T>.

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; 

Questo codice non è testato; né le prestazioni sono ottimali.

+2

Questo non funzionerà. Il metodo 'TryParse' modifica l'istanza che gli passi, che non è' t', ma una copia in scatola di esso. – svick

+0

Inoltre, pochi altri problemi (anche se non così grandi): è necessario inizializzare 't' prima di poterlo usare; è necessario specificare quale 'TryParse' vuoi (' int' ne ha due); devi fare qualcosa quando 'result' è' false'. – svick

6

Così, invece di scrivere:

double d = GetDoubleValue(str); 

si vuole essere in grado di scrivere questo?

double d = GetValue<double>(str); 

Anche se è possibile farlo funzionare, qual è il vantaggio? Personalmente non penso che questo sia un enorme miglioramento per il cliente. L'unico vantaggio è non dover implementare lo stesso metodo due volte. Ma data la semplicità del metodo e la difficoltà di implementare questo tipo di riutilizzo del codice, sembra ragionevole duplicare semplicemente quelle poche righe di codice.

Non sei solo con questo problema. Dai un'occhiata ad altri metodi nel framework .NET che funzionano su diversi tipi e vedi come lo hanno risolto. Ecco come BinaryReader consente di leggere diversi tipi:

Non è bello, ma in genere è così.


Per quanto riguarda l'aggiornamento, ho due ulteriori punti da fare.

Si dice che probabilmente si avranno più di due tipi e questo potrebbe portare a più duplicazioni dello stesso codice. Dato che il metodo è molto conciso e semplice, non sembra un problema copiare e incollare il codice. È una ragionevole eccezione alla regola secondo cui non dovresti copiare e incollare il codice, secondo me. L'approccio here riduce la quantità di duplicazione, anche se nella tua interfaccia pubblica hai ancora bisogno di molti metodi con nomi simili.

Tuttavia penso sia importante menzionare che sembra sbagliato utilizzare silenziosamente un valore predefinito nella propria situazione, come Lee ha già menzionato in un commento. Se un utente commette un errore durante l'immissione dei dati, dovrebbe ricevere un messaggio di errore. Dovresti utilizzare una sorta di framework di convalida per garantire che le stringhe siano valide e per informare il tuo utente qual è il problema se non sono valide. Dopo aver convalidato tutto è sicuro usare int.Parse anziché int.TryParse perché si sa che l'analisi avrà esito positivo. E se l'analisi fallisce, si tratta di una situazione eccezionale (un bug nella convalida), quindi sembra giusto che l'applicazione salti fuori un'eccezione e registri una traccia dello stack. Questo ti aiuterà a trovare e correggere il bug.

+0

@ Marco Byers Grazie. Mi hai capito al 100%. Hmm quindi non c'è modo di farlo ?????? – kashif

+0

@kashif: È difficile dire quale sia la soluzione "giusta" perché non hai detto perché ne hai bisogno. Perché non usi direttamente TryParse invece di nasconderlo dietro una chiamata di metodo? I progettisti .NET hanno riflettuto molto sull'API di TryParse e questo è ciò che hanno creato. In pratica stai semplicemente cambiando l'API in modo che tu restituisca 0 invece di false quando l'analisi fallisce. Come va meglio? Se vuoi davvero questo, come consentire al client di scegliere il valore predefinito? Esempio: 'TryParse (string s, int defaultValue)' e 'TryParse (string s, double defaultValue)'? –

2

Ho fatto un semplice esempio. Il codice non è ottimale, ma funziona come previsto.

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; 
    } 
} 
+0

Invece di fare affidamento solo sul conteggio dei parametri, preferisco usare l'overload di 'GetMethod()' che consente di specificare i parametri. Se lo fai, non dimenticare di usare ['MakeByRefType()'] (http://msdn.microsoft.com/en-us/library/system.type.makebyreftype.aspx). – svick

+0

@svick giusto! il codice è aggiornato. –

+0

funziona esattamente come volevo, ma preferirei usare i due metodi precedenti invece di usare questo enorme codice – kashif

0

Come si vede non è possibile limitare where T a Double o Int32, ecco perché probabilmente avete struct in quel luogo (int e doppio non è struct). I tipi di valore non possono essere utilizzati come restrizioni sui farmaci generici.

Quindi non è possibile digitare sicuro assicurarsi che il metodo TryParse esista. È necessario digitare typececk T e generare un'eccezione se è double o int come risposta di @ie.

Il mio suggerimento è quello di utilizzare la firma diversa:

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

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

In alternativa è possibile introdurre un'interfaccia IParsable. La casella double o int produce il risultato e implementa la conversione implicita in doppio o intero.

+0

'int' e' double' sono 'struct's, almeno per il scopi di vincoli di tipo generico. – svick

10

Sono d'accordo con Mark Byers. Probabilmente non è una buona idea provare a rendere questo metodo generico. Una piccola duplicazione del codice non farà male (a patto che sia davvero solo un po '). Il fatto che tu possa utilizzare qualsiasi struct con la tua versione generica, inoltre, non mi sembra una buona idea.

Se si vuole veramente fare questo, si può provare a usare il riflesso (come suggerito da Minustar), ma sarebbe brutto e lento.

Invece, è possibile utilizzare Convert.ChangeType():

private T GetValue<T>(string str) where T : struct 
{ 
    return (T)Convert.ChangeType(str, typeof(T)); 
} 
+0

Grazie per ChangeType – makoshichi

+0

abbiamo bisogno di provare ChangeType – CMS

7

vorrei non crei una proroga o metodo statico:

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; 
} 

è quindi necessario fornire la TryParse implementazione che si desidera utilizzare:

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

Attenzione che questa funzione si ritira silenziosamente urna un valore predefinito se l'analisi fallisce. È possibile che si desideri restituire default(T) se il delegato TryParse restituisce false.

+0

Questo mi sembra troppo duplicazione del codice per me. – svick

+0

@svick - Questo ha meno duplicazioni rispetto alla creazione di una funzione per ogni tipo che si desidera analizzare. Potrebbe essere eccessivo se si ha solo bisogno di gestire 'int's e' double's. – Lee

+2

+1 Questo approccio è mostrato qui: http://geekswithblogs.net/michelotti/archive/2008/10/05/tryparse-extension-methods.aspx Sono d'accordo che potrebbe essere eccessivo in questo caso, ma come una classe di utilità generale potrebbe essere utile –

0

Che dire di un metodo di estensione?

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; } 
    } 
} 

utilizzo:

int i = "123".TryParse<int>() ?? 0; 
Problemi correlati