2014-08-27 12 views
17

Per quanto ne so, int.TryParse(string, out int) esiste dal Framework 2.0. Così fa int?.Perché non T.TryParse restituisce un Nullable <T>

C'è una ragione per usare un parametro out invece di restituire un int? con HasValue insieme a true di false a seconda della capacità di convertire?

+3

Questa sarebbe una domanda per il team di sviluppo di .NET Framework. La maggior parte dei negozi finisce per scrivere un metodo di estensione 'stringa' chiamato qualcosa sulla falsariga di' ToNullableInt' che implementa il comportamento che si desidera. Non sarei sorpreso se una versione futura di C# supporti questo in modo nativo. –

+0

Forse perché le altre lingue non hanno una sintassi così semplice per Nullables. Per esempio. in C++/CLI devi scrivere 'Nullable result = int :: TryParse (...)'. – Stephan

+0

Che motivo avete per trattare qualsiasi stringa casuale come 'null'? La maggior parte degli usi potenziali che ho visto di una conversione da 'stringa' a' int? 'Sono per convertire una stringa vuota in' null', per convertire una stringa non vuota in un 'int' valido e per rifiutare la spazzatura casuale come' @ ($^V @ Q (% ^% (@ B^N ('. Questo non sarebbe adatto per' int.TryParse', indipendentemente dal tipo di ritorno.Puoi dare uno scenario ragionevole in cui 'TryParse 'restituire' T? 'è effettivamente desiderabile? – hvd

risposta

21

Non posso dire circa i motivi reali, ma vedo tre possibili ragioni:

1) Nullable types sono state introdotte in .NET 2.0, mentre il first TryParse methods erano già in giro dal .NET 1.1.Pertanto, quando sono stati introdotti i tipi nullable, era troppo tardi per una tale modifica dell'API; e le nuove classi non implementerebbero TryParse in modo diverso perché il modello era già stato impostato.

2) Non tutti i tipi possono essere utilizzati con Nullable structure, solo i tipi di valori possono. Tuttavia, esistono metodi che seguono lo schema Try* che devono restituire tipi di riferimento. Ad esempio, un dizionario può legittimamente contenere null come un elemento, quindi il suo TryGetValue method richiede un ulteriore modo per esprimere che una chiave non è stata trovata.

3) Il modo in cui i Try* -metodi sono scritte, è possibile scrivere codice come questo:

int myValue; 
if (int.TryParse("42", out myValue)) { 
    // do something with myValue 
} 
    // do something else 
} 

Ora, immaginate se TryParse restituito solo un int?. È possibile disporre della variabile myValue e perdere il risultato:

if (int.TryParse("42").HasValue) { 
    // do something with ... what? You didn't store the conversion result! 
} 
    // do something else 
} 

Oppure è possibile aggiungere una variabile annullabile:

int? myValue = int.TryParse("42"); 
if (myValue.HasValue) { 
    // do something with myValue.Value 
} 
    // do something else 
} 

Questo non è un vantaggio rispetto alla versione attuale più, e invece richiede la scrittura di myValue.Value in alcune istanze successive, dove altrimenti sarebbe sufficiente un semplice value. Si noti che in molti casi, è sufficiente che solo richieda l'informazione sull'esito dell'operazione per l'istruzione if.

+0

1) Non sapevo che i metodi' TryParse' esistessero nel Framework 1.0. 2) Non ci ho pensato. 3) Il numero di linee non è un problema. È solo che trovo la parola chiave 'out' un po '... innaturale da usare in C#. – krimog

+0

@krimog: Scusa, ho corretto da 1.0 a 1.1, poiché non sono riuscito a trovare un esempio 1.0 ora (ma la conclusione dell'istruzione è la stessa, ovviamente). –

+2

@krimog Perché utilizzare una parola chiave della lingua è "innaturale"? – asawyer

3

Per quanto riguarda motivi possiamo solo immaginare, ma alcuni possibili ragioni sono:

Assegnazione in testa: un valore boxed comporta qualche (piccolo) sovraccarico di prestazioni nel corso di un costruito in tipo.

No guadagni reali:

int res; 
if int.TryParse("one", out res) { 
    //something 
} 

non è molto peggio di

int? res = int.TryParse("one"); 
if (res.HasValue){ 
    int realres = res.Value 
    //something 
} 
+5

Questo non è "boxed"; almeno, non nel significato usuale del pugilato in .NET; un 'int?' richiede 8 byte nello stack; un 'int' e un' bool': richiedono 8 byte nello stack ... ma: un 'bool' e un' ref int' potrebbero effettivamente * 12 byte * nello stack (in x64); p Significato: il La versione 'out' richiede in realtà più incarichi, più ovviamente alcune operazioni di de-reference aggiuntive che non sono necessarie nell'altra. Il mio punto: entrambi hanno dei costi. –

+0

@MarcGravell: il valore non è 'out'? – krimog

+1

@krimog no, non è così; semplicemente passa l'indirizzo del campo/locale - che può essere un indirizzo in pila. In modo esplicito non crea una scatola. –

5

Ecco una citazione dal blog di Julie Lerman (Ritorno dal 2004):

ho giocato con nullable nei bit di anteprima di marzo, ma non ancora nel maggio e deluso con la corrente (ma previsto per il miglioramento serio dal bcl team !!!) prestazioni quando ho confrontato l'utilizzo di nullable<t> sulle opzioni correnti. Così, per esempio con tipi di valore:

confrontando myNullableInt.HasValue a (in VB) è myInt < 0

o con tipi di riferimento

confrontando myNullableThing.HasValue a “if not myThing=null

tipo nullable è attualmente molto molto più lento. Sono stato promesso da alcuni sul team BCL che il piano è quello di rendere il MOLTO più performante MUCH.

ho anche dato il suggerimento che, in futuro, di seguito sarà possibile:

Nullable<T> Parse(string value); 
Nullable<Int32> i = Int32.Parse(some String); 

e sarà più performante rispetto TryParse. Così anche questo sarà interessante.

mi assumo che, come sempre, i benefici superino i costi.

In ogni caso, nel prossimo C# vNext, si può fare:

DateTime.TryParse(s, out var parsedDateTime); 

Passando TryParse in un uno di linea.

+2

Ora puoi anche usare un one-liner se non hai bisogno di nullable: 'DateTime dt = DateTime.TryParse (s, out dt)? dt: DateTime.MinValue; ' –

+0

@TimSchmelter Hai ragione, ma una copertina piuttosto lunga :) –

21

Il semplice motivo è perché quando int.TryParse è stato aggiunto alla lingua, Nullable<T> non esisteva.

In this blog post by Eric Lippert, c'è una linea verso il fondo che recita:

La soluzione è quella di scrivere la propria versione metodo di estensione del TryParse il modo in cui sarebbe stato scritto se ci fosse stato tipi di valore nullable disponibili nel primo luogo

che chiarisce che tipi nullable non erano disponibili da utilizzare nell'implementazione originale di TryParse. Eric Lippert era nel team che ha scritto il compilatore C#, quindi direi che è una fonte piuttosto autorevole.

+1

Grazie per aver trovato il tempo per trovarlo;) – krimog

+4

Ancora una volta la fanbase di Erik Lippert di Stackoverflow colpisce ancora, basta menzionare Eric Lippert e gli upvotes istantanei. : D –

+2

Chi * può * rispondere alle domande anche quando non risponde alle domande? – Magus

4

Un altro motivo possibile:

Generics per # .NET e C nella loro forma attuale quasi non è accaduto: era una chiamata molto vicino, e la caratteristica quasi non ha fatto il taglio per Whidbey (Visual Studio 2005). Funzionalità come l'esecuzione del codice CLR nel database hanno avuto maggiore priorità.

...

In ultima analisi, un modello cancellazione dei farmaci generici sarebbero state adottate, come per Java, in quanto la squadra CLR non avrebbe mai perseguito una VM in-generici di progettazione senza aiuto esterno.

fonte: http://blogs.msdn.com/b/dsyme/archive/2011/03/15/net-c-generics-history-some-photos-from-feb-1999.aspx

Il mio punto dell'essere: la maggior parte dei cambiamenti del BCL (o almeno quelli non direttamente legato alla generici) probabilmente necessaria per lavorare sia con e senza farmaci generici, in caso in cui la feature è stata tagliata nell'RTM finale.

Ovviamente, questo ha anche senso dal punto di vista del client di chiamata: tutti i linguaggi che consumano (ok, non ce n'erano molti allora) sarebbero stati idealmente in grado di usarli - e i parametri out non erano così taglienti come i generici.

Problemi correlati