2010-08-02 14 views
5

Sto cercando di accelerare il seguente:Il modo più efficace per determinare se una lunghezza di stringa! = 0?

string s; //--> s is never null 

if (s.Length != 0) 
{ 
    <do something> 
} 

Il problema è, sembra che il .Length in realtà conta i caratteri della stringa, e questo è il modo più lavoro che ho bisogno. Qualcuno ha un'idea su come accelerare questo?

Oppure, c'è un modo per determinare se s [0] esiste, senza verificare il resto della stringa?

+1

Il problema del furto che affronti è molto probabile risolto altrove! Hai il profilo e scopri che il collo di bottiglia è davvero qui? –

+0

Per curiosità quale versione del framework stai prendendo di mira? –

+0

** NOTA ** per altri aspiranti-rispondenti: Eric ha pubblicato una risposta che chiarisce la sua domanda (kinda) sotto .... –

risposta

5

L'accesso alla proprietà Length non deve eseguire un conteggio: le stringhe .NET memorizzano un conteggio all'interno dell'oggetto.

Il SSCLI/Rotor source code contiene un commento interessante che suggerisce che String.Length è (a) efficiente e (b) magic:

// Gets the length of this string 
// 
/// This is a EE implemented function so that the JIT can recognise is specially 
/// and eliminate checks on character fetchs in a loop like: 
/// for(int I = 0; I < str.Length; i++) str[i] 
/// The actually code generated for this will be one instruction and will be inlined. 
// 
public extern int Length { 
    [MethodImplAttribute(MethodImplOptions.InternalCall)] 
    get; 
} 
7

String.IsNullOrEmpty è il metodo preferito per il controllo di stringhe di lunghezza null o zero.

Internamente, utilizzerà Lunghezza. Tuttavia, la proprietà Length per una stringa non dovrebbe essere calcolata al volo.

Se siete assolutamente certi che la stringa non sarà mai nulla e si ha qualche forte obiezione String.IsNullOrEmpty, il codice più efficiente mi viene in mente potrebbe essere:

if(s.Length > 0) 
{ 
    // Do Something 
} 

O, forse anche meglio:

if(s != "") 
{ 
    // Do Something 
} 
+0

Abbastanza corretto, ma è eccessivo se la stringa è nota per non essere nulla. –

+0

Non solo è eccessivo, ma suggerisce di pensare che la variabile * potrebbe * essere nulla. –

+0

quindi confrontalo con 'String.Empty' – ankitjaininfo

3

Ecco la funzione String.IsNullOrEmpty -

if (!String.IsNullOrEmpty(yourstring)) 
{ 
    // your code 
} 
+0

Per favore, spiega. – Hut8

+0

@bowenl, perché hai votato? – ankitjaininfo

+0

Perché inizialmente hai pubblicato qualcosa di diverso che non è stato compilato affatto. L'ho ritratta ora che l'hai aggiustata per corrispondere a ciò che qualcun altro ha postato prima di te. – Hut8

20

EDIT: Ora che ci hai fornito un po 'di contesto:

  • Cercando di riprodurre questo, non sono riuscito a trovare un collo di bottiglia nel string.Length a tutti. L'unico modo per renderlo più veloce era di commentare sia il test sia il corpo del blocco if, il che non è corretto. Solo commentare la condizione ha rallentato le cose, ovvero copiare incondizionatamente il riferimento è stato più lento rispetto alla verifica della condizione.

  • Come è stato sottolineato, l'utilizzo del sovraccarico di string.Split che rimuove le voci vuote per voi è la vera ottimizzazione dell'assassino.

  • Puoi andare oltre, evitando di creare un nuovo array di caratteri con solo uno spazio in ogni momento. Passerai sempre la stessa cosa in modo efficace, quindi perché non approfittarne?

  • Gli array vuoti sono effettivamente immutabili. È possibile ottimizzare il caso null/vuoto restituendo sempre la stessa cosa.

Il codice ottimizzato diventa:

private static readonly char[] Delimiters = " ".ToCharArray(); 
private static readonly string[] EmptyArray = new string[0]; 

public static string[] SplitOnMultiSpaces(string text) 
{ 
    if (string.IsNullOrEmpty(text)) 
    { 
     return EmptyArray; 
    } 

    return text.Split(Delimiters, StringSplitOptions.RemoveEmptyEntries); 
} 

String.Length assolutamente fa non contare le lettere nella stringa. Il valore è archiviato come un campo - anche se mi sembra di ricordare che il bit più in alto di quel campo è usato per ricordare se tutti i caratteri sono ASCII (o comunque usati per essere abilitati) per abilitare altre ottimizzazioni. Quindi l'accesso alla proprietà potrebbe aver bisogno di fare una maschera di bit, ma sarà comunque O (1) e mi aspetto che anche il JIT lo inline. (E 'implementato come un extern, ma si spera che non pregiudicherebbe il JIT in questo caso -. Ho il sospetto che sia un'operazione abbastanza comune per potenzialmente avere un supporto speciale)

Se sai già che la stringa non è nullo, quindi il test esistente di

if (s.Length != 0) 

è il modo migliore per andare se siete alla ricerca di prestazioni pure IMO. Personalmente, nella maggior parte dei casi mi piacerebbe scrivere:

if (s != "") 

per renderlo più chiaro che non siamo tanto interessati alla lunghezza come un valore come se questa è la stringa vuota. Sarà leggermente più lento del test di lunghezza, ma credo sia più chiaro. Come sempre, sceglierei il codice più chiaro finché non avremo dati di benchmark/profiling per indicare che questo veramente è un collo di bottiglia. So che la tua domanda riguarda esplicitamente la ricerca del test più efficiente, ma ho pensato di parlarne comunque. Avete prove che questo è un collo di bottiglia?

EDIT: Giusto per motivare più chiare per il mio suggerimento di non utilizzando string.IsNullOrEmpty: una chiamata a quel metodo mi suggerisce che il chiamante è esplicitamente cercando di trattare il caso in cui la variabile è nulla, altrimenti si wouldn' L'ho menzionato. Se a questo punto del codice conta come un bug se la variabile è null, allora non dovresti provare a gestirla come un caso normale.

In questa situazione, il controllo è in realtà Lengthmeglio in un modo che il test di ineguaglianza ho suggerito: agisce come asserzione implicita che la variabile non è nullo. Se si verifica un bug e è null, il test genererà un'eccezione e il bug verrà rilevato in anticipo. Se si utilizza il test di uguaglianza, considererà null come diverso dalla stringa vuota, quindi entrerà nel corpo della propria istruzione "if". Se si utilizza string.IsNullOrEmpty, verrà considerato nulla come vuoto, quindi non entrerà nel blocco.

+2

Penso che la cosa migliore di questa sia l'ultima riga. – msarchet

+0

@msarchet: è un peccato che ho modificato la risposta mentre stavi scrivendo quel commento. Immagino tu voglia dire "Hai prove che questo * è * un collo di bottiglia?" –

+0

sì, anche se è ancora valido – msarchet

0

Come sempre con performace: benchmark.
con C# 3.5 o prima, ti consigliamo di provare yourString.Length vs String.IsNullOrEmpty(yourString)

utilizzando C# 4, fare entrambe le cose di cui sopra e aggiungere String.IsNullOrWhiteSpace(yourString)

Naturalmente, se si conosce la stringa non sarà mai vuoto, potresti semplicemente tentare di accedere a s[0] e gestire l'eccezione quando non è presente. Questo non è normalmente buona pratica, ma potrebbe essere più vicino a quello che ti serve (se s dovrebbe sempre avere un valore non vuoto).

+0

'IsNullOrWhitespace' darà la risposta sbagliata, dati i requisiti nella domanda. –

0
 for (int i = 0; i < 100; i++) 
     { 
      System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); 
      string s = "dsfasdfsdafasd"; 

      timer.Start(); 
      if (s.Length > 0) 
      { 
      } 

      timer.Stop(); 
      System.Diagnostics.Debug.Write(String.Format("s.Length != 0 {0} ticks  ", timer.ElapsedTicks)); 

      timer.Reset(); 
      timer.Start(); 
      if (s == String.Empty) 
      { 
      } 

      timer.Stop(); 
      System.Diagnostics.Debug.WriteLine(String.Format("s== String.Empty {0} ticks", timer.ElapsedTicks)); 
     } 

uso del cronometro del s.length! = 0 richiede meno zecche poi s == String.Empty

dopo posso correggere il codice

1

Basato sul vostro intento descritto nella sua risposta, perché non basta provare a utilizzare questa opzione built-in Split:

s.Split(new[]{" "}, StringSplitOptions.RemoveEmptyEntries); 
0

basta usare String.Split (nuovo char [] {' '}, StringSplitOptions.RemoveEmptyEntries) e lo farà tutto per voi.