2009-05-07 6 views
17

Se sto costruendo una stringa utilizzando un oggetto StringBuilder in un metodo, avrebbe senso:String o StringBuilder restituiscono valori?

Restituire l'oggetto StringBuilder e lasciare che il codice chiamante chiami ToString()?

return sb; 

OPPURE Restituire la stringa chiamando ToString().

return sb.ToString(); 

Suppongo che faccia la differenza se restituiamo stringhe piccole o grandi. Cosa sarebbe appropriato in ogni caso? Grazie in anticipo.

Modifica: Non ho intenzione di modificare ulteriormente la stringa nel codice chiamante, ma buon punto Colin Burnett.

Principalmente, è più efficiente restituire l'oggetto StringBuilder o la stringa? Verrà restituito un riferimento alla stringa o una copia?

risposta

21

Restituire StringBuilder se si desidera modificare ulteriormente la stringa, altrimenti restituire la stringa. Questa è una domanda API.

Riguardo all'efficienza. Poiché questa è una domanda vaga/generica senza specifiche, ritengo che mutable vs. immutable sia più importante della performance. La mutabilità è un problema dell'API che consente all'API di restituire oggetti modificabili. La lunghezza della stringa è irrilevante a questo.

Detto questo. Se si guarda a StringBuilder.ToString con riflettore:

public override string ToString() 
{ 
    string stringValue = this.m_StringValue; 
    if (this.m_currentThread != Thread.InternalGetCurrentThread()) 
    { 
     return string.InternalCopy(stringValue); 
    } 
    if ((2 * stringValue.Length) < stringValue.ArrayLength) 
    { 
     return string.InternalCopy(stringValue); 
    } 
    stringValue.ClearPostNullChar(); 
    this.m_currentThread = IntPtr.Zero; 
    return stringValue; 
} 

Si può vedere se può fare una copia, ma se si modifica con lo StringBuilder allora farà una copia allora (questo è quello che posso dire al punto di m_currentThread è perché Append verifica questo e lo copierà se non corrisponde al thread corrente).

Suppongo che la fine di questo è che se non si modifica StringBuilder, non si copia la stringa e la lunghezza è irrilevante per l'efficienza (a meno che non si prenda il 2 ° se).

UPDATE

System.String è una classe che significa che è un tipo di riferimento (al contrario di tipo valore) così "stringa foo;" è essenzialmente un puntatore. (Quando passi una stringa in un metodo passa il puntatore, non una copia.) System.String è mutabile dentro mscorlib ma immutabile al di fuori di esso, che è il modo in cui StringBuilder può manipolare una stringa.

Quindi, quando viene chiamato ToString(), restituisce l'oggetto stringa interno per riferimento. A questo punto non puoi modificarlo perché il tuo codice non è in mscorlib. Impostando il campo m_currentThread su zero, qualsiasi ulteriore operazione su StringBuilder causerà la copia dell'oggetto stringa in modo che possa essere modificato e senza modificare l'oggetto stringa restituito in ToString(). Considerate questo:

StringBuilder sb = new StringBuilder(); 
sb.Append("Hello "); 

string foo = sb.ToString(); 

sb.Append("World"); 

string bar = sb.ToString(); 

Se StringBuilder non ha fatto una copia poi alla fine foo sarebbe "Ciao Mondo", perché lo StringBuilder modificato. Ma dal momento che ha fatto una copia, quindi foo è ancora solo "Ciao" e la barra è "Hello World".

Questo chiarisce l'intera faccenda di ritorno/riferimento?

+0

@SkippyFire chiede efficienza. –

+0

-1. Se stai andando a modificare ulteriormente la stringa, dovrebbe essere ulteriormente modificata all'interno del metodo. Cosa succede se questo metodo viene chiamato da più posti e la logica di manipolazione delle stringhe cambia? Non sarebbe bello aggiornare la logica in più punti. –

+1

Nick, stai dividendo i capelli. Se questo è in una classe e in un metodo privato, si può benissimo passare lo StringBuilder a molti metodi per costruire la stringa finale. Potresti fare la stessa cosa quando ogni metodo restituisce una stringa e le concatena. SkippyFire non era specifico per come veniva usato. È piuttosto semplice: se hai bisogno di mutabile, restituisci mutabile, se non hai bisogno di mutabile allora non tornare mutabile. –

5

Non penso che le prestazioni debbano essere un fattore in questa domanda. In entrambi i casi qualcuno chiamerà sb.ToString() in modo che tu possa prendere il colpo da qualche parte.

La domanda più importante è qual è l'intenzione del metodo e lo scopo. Se questo metodo fa parte di un builder, è possibile restituire il generatore di stringhe. Altrimenti restituirei una stringa.

Se questo è parte di un'API pubblica avrei puntato verso restituendo una stringa al posto del costruttore.

+0

La questione dell'efficienza, quindi, è se si modificherà la stringa più che sarebbe meglio averla già in una forma mutevole (cioè, StringBuilder) per evitare di copiare in un nuovo StringBuilder per modificarlo. Solo perché in qualche punto della linea chiamerai ToString non significa che non vuoi evitare la copia intermedia. –

+1

@Colin Burnett, in termini di prestazioni, se è richiesta la mutevolezza, restituire StringBuilder è una soluzione pratica, ma non è una buona soluzione. È preferibile riscrivere il chiamante per supportare un modello che costruisca l'oggetto completamente e rimanda la costruzione della stringa a una singola chiamata. Ci sono due svantaggi nel restituire StringBuilder, in primo luogo, ti accoppia a un dettaglio di implementazione. In secondo luogo, si allontana dall'abilitare un buon approccio OOP, che ostacolerà la redditività della soluzione come API (e ti bloccherà l'implementazione procedurale). –

+0

Michael, il mio unico punto in quel commento è stato che "qualcuno sta per chiamare sb.ToString() "manca il punto di copia intermedia che influirà sulle prestazioni. A volte sì, potresti voler prendere il colpo per avere una migliore API o essere motivato a trovare una soluzione diversa da" string o StringBuilder ". –

3

Direi che il metodo deve restituire sb.ToString(). Se la logica che circonda la creazione dell'oggetto StringBuilder() dovrebbe cambiare in futuro, ha senso che sia cambiata nel metodo non in ogni scenario che chiama il metodo e poi continua a fare qualcos'altro

0

Se è necessario aggiungere più elementi alla stringa e utilizzare altre funzionalità correlate a stringbuilder, restituire lo stringbuilder. Altrimenti se stai usando la stringa stessa, restituisci la stringa.

Ci sono altre considerazioni tecniche, ma questo è il problema più alto.

1

Dipende da cosa si sta progettando di fare con l'output. Vorrei restituire una stringa personalmente. In questo modo se hai bisogno di cambiare il metodo in fondo alla strada per non usare un costruttore di stringhe, puoi come non lo rimarrai bloccato come valore di ritorno.

momento di pensarci, per il momento, la risposta è molto più chiara. La domanda di chiedere quale dovrebbe essere restituito risponde davvero alla domanda. L'oggetto di ritorno dovrebbe essere una stringa. Il motivo è che se stai facendo la domanda, "C'è un motivo per restituire l'oggetto StringBuilder quando una stringa lo farà?" allora la risposta è no. Se ci fosse un motivo, la restituzione della stringa sarebbe fuori questione, poiché sono necessari i metodi e le proprietà del costruttore di stringhe.

1

Penso che dipenda da ciò che si sta facendo con la stringa una volta lasciato il metodo. Se vuoi continuare ad aggiungerlo, potresti prendere in considerazione la possibilità di restituire un stringbuilder per una maggiore efficienza. Se hai sempre intenzione di chiamare .ToString() su di esso, dovresti farlo all'interno del metodo per una migliore incapsulamento.

1

vorrei tornare un string in quasi tutte le situazioni, soprattutto se il metodo è parte di un'API pubblica.

Un'eccezione sarebbe se il metodo è solo una parte di un più ampio processo, privata "costruttore" e il codice chiamante farà ulteriori manipolazioni. In tal caso, prenderei in considerazione la possibilità di restituire uno StringBuilder.

1

Dal momento che il non andare a modificarlo più

return sb.ToString(); 

dovrebbe essere più efficiente

1

Riportare lo sb.ToString(). Il tuo metodo dovrebbe concentrarsi solo sulla cosa in mano (In questo caso costruisci una stringa) e non essere restituito per essere ulteriormente manipolato IMO, potresti entrare in tutti i tipi di problemi con esso non essere smaltiti.

3

StringBuilder è un dettaglio di implementazione del metodo. Dovresti restituire una stringa finché non diventa un problema di prestazioni, a quel punto dovresti esplorare un altro pattern (come visitor Pattern) che può aiutarti a introdurre l'indirezione e proteggerti dalle decisioni di implementazione interne.

Le stringhe sono sempre memorizzate nell'heap, quindi verrà restituito un riferimento se il tipo restituito è stringa. Tuttavia, non è possibile contare su due stringhe identiche per avere riferimenti identici. In generale, è sicuro pensare a una stringa come se fosse un tipo di valore anche se in realtà è un tipo di riferimento.

+1

+1 per i dettagli di implementazione –

0

Il metodo è stato assegnato a un'attività concreta e dovrebbe essere previsto completarlo e restituire il risultato finale che non richiede ulteriori elaborazioni. Restituisci StringBuilder solo quando ne hai veramente bisogno. In tal caso, aggiungi qualcosa al nome del metodo per indicare che stai restituendo qualcosa di speciale.