Pergunta

Suponha que eu tenha um stringbuilder em C# que faz isso:

StringBuilder sb = new StringBuilder();
string cat = "cat";
sb.Append("the ").Append(cat).(" in the hat");
string s = sb.ToString();

seria tão eficiente, ou mais eficiente, como tendo:

string cat = "cat";
string s = String.Format("The {0} in the hat", cat);

Se assim for, por quê?

EDITAR

Depois de algumas respostas interessantes, que eu percebi que eu provavelmente deveria ter sido um pouco mais claro o que eu estava pedindo.Eu não era muito de pedir para o qual foi mais rápido concatenação de uma string, mas o que é mais rápido injetar uma cadeia de caracteres em outra.

Em ambos os casos acima, eu quero injetar uma ou mais cadeias no meio de um modelo pré-definido de seqüência de caracteres.

Desculpem a confusão

Foi útil?

Solução

NOTA: Esta resposta foi escrito quando .NET 2.0 foi a versão atual.Isso pode já não se aplicar a versões posteriores.

String.Format usa um StringBuilder internamente:

public static string Format(IFormatProvider provider, string format, params object[] args)
{
    if ((format == null) || (args == null))
    {
        throw new ArgumentNullException((format == null) ? "format" : "args");
    }

    StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8));
    builder.AppendFormat(provider, format, args);
    return builder.ToString();
}

O código acima é um trecho do mscorlib, portanto, a questão torna-se "é StringBuilder.Append() mais rápido do que StringBuilder.AppendFormat()"?

Sem benchmarking eu provavelmente diria que o exemplo de código acima seria executado mais rapidamente, usando .Append().Mas é um palpite, tente benchmarking e/ou criação de perfis para obter uma comparação adequada.

Este sujeito, Jerry Dixon, fez alguns benchmarking:

http://jdixon.dotnetdevelopersjournal.com/string_concatenation_stringbuilder_and_stringformat.htm

Atualizado:

Infelizmente o link acima já morreu.No entanto, ainda há uma cópia no Caminho de Volta da Máquina:

http://web.archive.org/web/20090417100252/http://jdixon.dotnetdevelopersjournal.com/string_concatenation_stringbuilder_and_stringformat.htm

No final do dia, depende se sua cadeia de caracteres de formatação vai ser chamado repetidamente, i.é.você está fazendo alguns graves de processamento de texto mais de 100 megabytes de texto, ou se ele está sendo chamado quando um usuário clica em um botão e agora novamente.A menos que você esteja fazendo uma enorme lote de processamento trabalho que eu ia ficar com Cadeia.O formato, ela ajuda a legibilidade do código.Se você suspeitar de um perf gargalo, em seguida, ficar um profiler no seu código e ver de onde ele realmente está.

Outras dicas

Do Documentação do MSDN:

O desempenho de uma operação de concatenação de uma String de texto ou objeto StringBuilder, depende de como, muitas vezes, uma alocação de memória ocorre.Uma Seqüência de operação de concatenação sempre aloca a memória, enquanto um StringBuilder operação de concatenação apenas aloca a memória, se o objeto StringBuilder memória intermédia é demasiado pequena para acomodar os novos dados.Consequentemente, a classe String é preferível para uma operação de concatenação se um número fixo de objetos String são concatenadas.Nesse caso, o indivíduo concatenação operações podem até ser combinados em uma única operação pelo compilador.Um objeto StringBuilder é preferível para uma operação de concatenação se um número arbitrário de seqüências de caracteres são concatenadas;por exemplo, se um loop concatena um número aleatório de seqüências de caracteres de entrada do usuário.

Eu corri algumas rápidas referências de desempenho, e para 100.000 operações, calculados sobre 10 execuções, o primeiro método (String Builder) leva quase a metade do tempo da segunda (Formato de Seqüência de caracteres).

Então, se isso é frequente, não importa.Mas, se é uma operação comum, então você pode querer usar o primeiro método.

Eu esperaria Seqüência de caracteres.Formato para ser mais lento - o que tem para analisar a seqüência de caracteres e em seguida, concatená-lo.

Algumas notas:

  • Formato é o caminho para ir para o utilizador-cadeias de caracteres visíveis em aplicações profissionais;isso evita bugs de localização
  • Se você sabe o comprimento da seqüência de caracteres resultante de antemão, use o StringBuilder(Int32) construtor para definir a capacidade de

Se apenas porque seqüência de caracteres.Formato não exatamente o que você pode pensar, aqui é uma reedição dos ensaios 6 anos mais tarde Net45.

Concat é ainda mais rápido, mas realmente é menor do que 30% de diferença.StringBuilder e Formato diferentes por quase 5% a 10%.Eu tenho variações de 20% executar os testes algumas vezes.

Milissegundos, um milhão de iterações:

  • Concatenação:367
  • New stringBuilder para cada chave:452
  • Em Cache StringBuilder:419
  • seqüência de caracteres.Formato:475

A lição que eu tirar é que a diferença de desempenho é trivial e por isso não deve impedi-lo de escrever a mais simples de um código legível você pode.Que o meu dinheiro é muitas vezes mas não sempre a + b + c.

const int iterations=1000000;
var keyprefix= this.GetType().FullName;
var maxkeylength=keyprefix + 1 + 1+ Math.Log10(iterations);
Console.WriteLine("KeyPrefix \"{0}\", Max Key Length {1}",keyprefix, maxkeylength);

var concatkeys= new string[iterations];
var stringbuilderkeys= new string[iterations];
var cachedsbkeys= new string[iterations];
var formatkeys= new string[iterations];

var stopwatch= new System.Diagnostics.Stopwatch();
Console.WriteLine("Concatenation:");
stopwatch.Start();

for(int i=0; i<iterations; i++){
    var key1= keyprefix+":" + i.ToString();
    concatkeys[i]=key1;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

Console.WriteLine("New stringBuilder for each key:");
stopwatch.Restart();

for(int i=0; i<iterations; i++){
    var key2= new StringBuilder(keyprefix).Append(":").Append(i.ToString()).ToString();
    stringbuilderkeys[i]= key2;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

Console.WriteLine("Cached StringBuilder:");
var cachedSB= new StringBuilder(maxkeylength);
stopwatch.Restart();

for(int i=0; i<iterations; i++){
    var key2b= cachedSB.Clear().Append(keyprefix).Append(":").Append(i.ToString()).ToString();
    cachedsbkeys[i]= key2b;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

Console.WriteLine("string.Format");
stopwatch.Restart();

for(int i=0; i<iterations; i++){
    var key3= string.Format("{0}:{1}", keyprefix,i.ToString());
    formatkeys[i]= key3;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

var referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway= concatkeys.Union(stringbuilderkeys).Union(cachedsbkeys).Union(formatkeys).LastOrDefault(x=>x[1]=='-');
Console.WriteLine(referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway);

Eu acho que, na maioria dos casos como este clareza, e não a eficiência, deve ser a sua maior preocupação.A menos que você está esmagando juntos toneladas de cadeias de caracteres, ou construindo algo para um menor alimentado dispositivo móvel, isso provavelmente não vai fazer muita mossa na sua velocidade.

Descobri que, em casos onde estou construindo cadeias de uma forma bastante linear, fazendo reta concatenações ou usar StringBuilder é a sua melhor opção.Eu sugiro que esta nos casos em que a maioria da seqüência que você está construindo é dinâmico.Uma vez que muito pouco do texto é estático, a coisa mais importante é que, é claro, onde cada pedaço de texto dinâmico está sendo posto em caso precisa ser atualizado no futuro.

Por outro lado, se você está falando sobre um grande bloco de texto estático com duas ou três variáveis, mesmo se ele é um pouco menos eficiente, acho que a clareza que você ganha de seqüência de caracteres.Formato faz pena.Eu usei isso no início desta semana, quando precisar coloque um pouco de dinâmica de texto no centro da página 4 do documento.Vai ser mais fácil para atualizar o grande bloco de texto se o seu em um pedaço de ter para actualizar três peças que você concatenar juntos.

Seqüência de caracteres.Usa o formato de StringBuilder internamente...então, logicamente, que leva à idéia de que ele seria um pouco menos de performance, devido à sobrecarga.No entanto, uma simples concatenação de seqüência de caracteres é o método mais rápido de injetar uma seqüência de caracteres entre dois others...by um grau significativo.Esta evidência foi demonstrada por Rico Mariani, em sua primeira apresentação Quiz, anos atrás.O simples fato é que concatenações...quando o número de seqüência de caracteres de peças é conhecido (sem limitação..você pode concatenar um mil peças...contanto que você sabe que é sempre de 1000 peças)...são sempre mais rápido do que StringBuilder ou Seqüência de caracteres.Formato.Eles podem ser feitos com uma única alocação de memória de um de uma série de cópias de memória. Aqui é a prova de

E aqui está o código real para alguns de Seqüência de caracteres.Concat métodos, o que, em última análise, chamada de FillStringChecked que utiliza ponteiros para copiar a memória (extraído através de Refletor):

public static string Concat(params string[] values)
{
    int totalLength = 0;

    if (values == null)
    {
        throw new ArgumentNullException("values");
    }

    string[] strArray = new string[values.Length];

    for (int i = 0; i < values.Length; i++)
    {
        string str = values[i];
        strArray[i] = (str == null) ? Empty : str;
        totalLength += strArray[i].Length;

        if (totalLength < 0)
        {
            throw new OutOfMemoryException();
        }
    }

    return ConcatArray(strArray, totalLength);
}

public static string Concat(string str0, string str1, string str2, string str3)
{
    if (((str0 == null) && (str1 == null)) && ((str2 == null) && (str3 == null)))
    {
        return Empty;
    }

    if (str0 == null)
    {
        str0 = Empty;
    }

    if (str1 == null)
    {
        str1 = Empty;
    }

    if (str2 == null)
    {
        str2 = Empty;
    }

    if (str3 == null)
    {
        str3 = Empty;
    }

    int length = ((str0.Length + str1.Length) + str2.Length) + str3.Length;
    string dest = FastAllocateString(length);
    FillStringChecked(dest, 0, str0);
    FillStringChecked(dest, str0.Length, str1);
    FillStringChecked(dest, str0.Length + str1.Length, str2);
    FillStringChecked(dest, (str0.Length + str1.Length) + str2.Length, str3);
    return dest;
}

private static string ConcatArray(string[] values, int totalLength)
{
    string dest = FastAllocateString(totalLength);
    int destPos = 0;

    for (int i = 0; i < values.Length; i++)
    {
        FillStringChecked(dest, destPos, values[i]);
        destPos += values[i].Length;
    }

    return dest;
}

private static unsafe void FillStringChecked(string dest, int destPos, string src)
{
    int length = src.Length;

    if (length > (dest.Length - destPos))
    {
        throw new IndexOutOfRangeException();
    }

    fixed (char* chRef = &dest.m_firstChar)
    {
        fixed (char* chRef2 = &src.m_firstChar)
        {
            wstrcpy(chRef + destPos, chRef2, length);
        }
    }
}

Então:

string what = "cat";
string inthehat = "The " + what + " in the hat!";

Divirta-se!

Ah, também, a mais rápida seria:

string cat = "cat";
string s = "The " + cat + " in the hat";

Ela realmente depende.Para pequenas cadeias com alguns concatenações, é realmente mais rápido apenas para acrescentar as seqüências de caracteres.

String s = "String A" + "String B";

Mas para a maior seqüência de caracteres (muito, muito grandes cadeias de caracteres), é mais eficiente usar StringBuilder.

Em ambos os casos acima, eu quero injetar uma ou mais cadeias no meio de um modelo pré-definido de seqüência de caracteres.

Nesse caso, eu gostaria de sugerir a Cadeia.O formato é o mais rápido, porque ele é o design para a exata finalidade.

Isso realmente depende do seu padrão de uso.
Um detalhado referência entre string.Join, string,Concat e string.Format pode ser encontrado aqui: Seqüência de caracteres.O formato não é Adequado para a atividade Madeireira Intensiva

Gostaria de sugerir que não, pois a Seqüência de caracteres.O formato não foi projetado para concatenação, ele era o design para formatar a saída de várias entradas, tal como uma data.

String s = String.Format("Today is {0:dd-MMM-yyyy}.", DateTime.Today);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top