2009-05-07 8 views
17

Si estoy construyendo una cadena usando un objeto StringBuilder en un método, ¿tendría sentido:Valores de retorno String o StringBuilder?

Devolver el objeto StringBuilder y dejar que el código de llamada llame a ToString()?

return sb; 

O Devuelva la cadena llamando a ToString().

return sb.ToString(); 

supongo que alguna diferencia si estamos volviendo pequeñas, o grandes cadenas. ¿Qué sería apropiado en cada caso? Gracias por adelantado.

Editar: No planeo modificar aún más la cadena en el código de llamada, pero es bueno señalar a Colin Burnett.

Principalmente, ¿es más eficiente devolver el objeto StringBuilder o la cadena? ¿Se devolvería una referencia a la cadena, o una copia?

Respuesta

21

Devuelve StringBuilder si vas a modificar aún más la cadena; de lo contrario, devuelve la cadena. Esta es una pregunta API.

En cuanto a la eficiencia. Dado que esta es una pregunta vaga/general sin ningún detalle, entonces creo que mutable vs. inmutable es más importante que el rendimiento. La capacidad de mutación es un problema de API que permite que su API devuelva objetos modificables. La longitud de la cadena es irrelevante para esto.

Dicho eso. Si nos fijamos en StringBuilder.ToString con reflector:

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

se puede ver que se puede hacer una copia, pero si lo modifica con el StringBuilder continuación, se hará una copia a continuación (esto es lo que puedo decir el punto de m_currentThread es porque Append lo verifica y lo copiará si no coincide con el hilo actual).

Supongo que el final de esto es que si no modifica StringBuilder, entonces no copia la cadena y la longitud es irrelevante para la eficiencia (a menos que haga clic en 2nd si).

ACTUALIZACIÓN

System.String es una clase que significa que es un tipo de referencia (a diferencia de tipo de valor) de manera "foo cadena;" es esencialmente un puntero. (Cuando pasa una cadena a un método, pasa el puntero, no una copia.) System.String es mutable dentro de mscorlib pero inmutable fuera de él, que es cómo StringBuilder puede manipular una cadena.

Así que cuando se llama a ToString() devuelve su objeto de cadena interno por referencia. En este punto, no puede modificarlo porque su código no está en mscorlib. Al establecer el campo m_currentThread en cero, cualquier operación adicional en StringBuilder hará que copie el objeto de cadena para que se pueda modificar y no modifique el objeto de cadena que devolvió en ToString(). Considere esto:

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

string foo = sb.ToString(); 

sb.Append("World"); 

string bar = sb.ToString(); 

Si StringBuilder no hizo una copia a continuación, en el extremo foo sería "Hola mundo" debido a que el StringBuilder lo modificó. Pero como hizo una copia, foo sigue siendo solo "Hello" y la barra es "Hello World".

¿Eso aclara todo el asunto de devolución/referencia?

+0

@SkippyFire pregunta por la eficiencia. –

+0

-1. Si va a modificar aún más la cadena, debe modificarse aún más dentro del método. ¿Qué ocurre si se llama a este método desde varios lugares y la lógica de manipulación de cadenas cambia? No sería bueno actualizar la lógica en varios lugares –

+1

Nick, estás dividiendo los pelos. Si se trata de una clase y un método privado, puede pasar StringBuilder a muchos métodos para construir la cadena final. Podría hacer lo mismo donde cada método devuelve una cadena y los concatena. SkippyFire no fue específico en absoluto en cómo se usa esto. Es bastante simple: si necesita mutable, entonces devuelva mutable, si no necesita mutable, entonces no devuelva mutable. –

5

No creo que el rendimiento deba ser un factor en esta pregunta. De cualquier manera, alguien va a llamar a sb.ToString() para que vayas a tomar el golpe en algún lugar.

La pregunta más importante es cuál es la intención del método y el propósito. Si este método es parte de un generador, puede devolver el generador de cadenas. De lo contrario, devolvería una cadena.

Si esto es parte de una API pública, me inclinaría por devolver una cadena en lugar del constructor.

+0

La cuestión de eficiencia, entonces, es si va a modificar la cadena más de lo que sería mejor tenerla ya en forma mutable (es decir, StringBuilder) para evitar copiar en un nuevo StringBuilder para modificarla. El hecho de que en algún punto de la línea llame a ToString no significa que no desee evitar la copia intermedia. –

+1

@Colin Burnett, en términos de rendimiento, si se requiere mutabilidad, devolver StringBuilder es una solución práctica, pero no es buena. Es preferible reescribir la persona que llama para que admita un patrón que crea el objeto por completo y difiere la construcción de la cadena a una sola llamada. Hay dos inconvenientes para devolver StringBuilder, primero, lo conecta con un detalle de implementación. En segundo lugar, se abstiene de permitir un buen enfoque OOP, lo que obstaculizará la viabilidad de la solución como API (y lo bloqueará a una implementación de procedimientos). –

+0

Michael, mi único punto en ese comentario fue que "alguien llamará a sb.A ToString() le falta el punto de copia intermedia que afectará el rendimiento. A veces, sí, es posible que desee tomar el golpe para tener una mejor API o estar motivado para encontrar una solución que no sea "cadena o StringBuilder". –

3

Yo diría que el método debería devolver sb.ToString(). Si la lógica que rodea la creación del objeto StringBuilder() debe cambiar en el futuro, tiene sentido para mí que se cambie en el método no en cada escenario que llama al método y luego pasa a hacer otra cosa

0

If necesita agregar más cosas a la cadena y usar otra funcionalidad relacionada con stringbuilder, devolver el generador de cadenas. De lo contrario, si solo está utilizando la cadena en sí, devuelva la cadena.

Existen otras consideraciones más técnicas, pero esa es la preocupación más alta.

1

Depende de lo que planeas hacer con la salida. Me gustaría devolver una cadena personalmente. De esta forma, si necesita cambiar el método más adelante para no utilizar un generador de cuerdas, puede hacerlo ya que no tendrá que utilizarlo como valor de retorno.

Al pensarlo por el momento, la respuesta es mucho más clara. La pregunta de cuál debe ser devuelto realmente responde la pregunta. El objeto de retorno debe ser una cadena. La razón es que si hace la pregunta, "¿Existe alguna razón para devolver el objeto StringBuilder cuando lo haga una cadena?" Entonces la respuesta es no. Si hubiera una razón, entonces devolver cadena estaría fuera de discusión, porque los métodos y propiedades del generador de cadenas son necesarios.

1

Creo que depende de lo que esté haciendo con la cadena una vez que abandona el método. Si va a seguir adjuntándolo, puede considerar la posibilidad de devolver un generador de cadenas para una mayor eficacia. Si siempre va a llamar a .ToString() en él, debe hacerlo dentro del método para una mejor encapsulación.

1

Devolvería un string en casi todas las situaciones, especialmente si el método es parte de una API pública.

Una excepción sería si su método es solo una parte de un proceso más grande y privado de "generador" y el código de llamada hará más manipulaciones. En ese caso, entonces tal vez considere devolver un StringBuilder.

1

Desde tu no va a modificarlo más

return sb.ToString(); 

debería ser más eficiente

1

Volver al sb.ToString(). Su método debe concentrarse solo en lo que tiene a mano (en este caso construir una cadena) y no volver a ser manipulado más allá de la OMI, podría tener todo tipo de problemas con que no se elimine.

3

StringBuilder es un detalle de implementación de su método. Debería devolver la cadena hasta que se convierta en un problema de rendimiento, en cuyo punto debe explorar otro patrón (como visitor Pattern) que puede ayudarlo a introducir la indirección y protegerlo de las decisiones internas de implementación.

Las cadenas siempre se almacenan en el montón, por lo que tendrá una referencia devuelta si el tipo de devolución es una cadena. Sin embargo, no puede contar en dos cadenas idénticas para tener referencias idénticas. En general, es seguro pensar en una cadena como si fuera un tipo de valor a pesar de que en realidad es un tipo de referencia.

+1

+1 para detalles de implementación –

0

El método recibió una tarea concreta y se espera que lo complete y devuelva el resultado final que no requiere un procesamiento posterior. Solo devuelve StringBuilder cuando realmente lo necesites. En ese caso, también agregue algo al nombre del método para indicar que está devolviendo algo especial.

Cuestiones relacionadas