vinagre truco saber reconocer plata para oro limon laminado forma con como chapa casera 14k c# performance optimization coding-style string

c# - truco - como saber si es plata con limon



¿Cuál es la forma más eficiente de determinar si una cadena no recortada está vacía en C#? (8)

Tengo una cadena que puede tener espacios en blanco alrededor y quiero verificar si está esencialmente vacía.

Hay varias maneras de hacer esto:

1 if (myString.Trim().Length == 0) 2 if (myString.Trim() == "") 3 if (myString.Trim().Equals("")) 4 if (myString.Trim() == String.Empty) 5 if (myString.Trim().Equals(String.Empty))

Soy consciente de que este suele ser un caso claro de optimización prematura, pero tengo curiosidad y hay una posibilidad de que esto se haga lo suficiente como para tener un impacto en el rendimiento.

Entonces, ¿cuál de estos es el método más eficiente?

¿Hay algún método mejor en el que no haya pensado?

Editar: notas para los visitantes a esta pregunta:

  1. Ha habido algunas investigaciones increíblemente detalladas sobre esta cuestión, especialmente de Andy y Jon Skeet.

  2. Si te has topado con la pregunta mientras buscas algo, bien vale la pena leer al menos las publicaciones de Andy y Jon en su totalidad.

Parece que hay unos pocos métodos muy eficientes y el más eficiente depende del contenido de las cadenas con las que tengo que lidiar.

Si no puedo predecir las cadenas (que no puedo en mi caso), los métodos IsEmptyOrWhiteSpace de Jon parecen ser más rápidos en general.

Gracias a todos por su aporte. Voy a seleccionar la respuesta de Andy como la "correcta" simplemente porque merece el impulso de la reputación por el esfuerzo que hizo y Jon ya tiene una reputación de mil setecientos millones.


(EDITAR: Ver la parte inferior de la publicación para los puntos de referencia sobre diferentes micro-optimizaciones del método)

No lo recortes, ya que podría crear una nueva cuerda que en realidad no necesitas. En su lugar, busque en la cadena los caracteres que no sean espacios en blanco (para la definición que desee). Por ejemplo:

public static bool IsEmptyOrWhitespace(string text) { // Avoid creating iterator for trivial case if (text.Length == 0) { return true; } foreach (char c in text) { // Could use Char.IsWhiteSpace(c) instead if (c=='' '' || c==''/t'' || c==''/r'' || c==''/n'') { continue; } return false; } return true; }

También puede considerar lo que quiere que haga el método si el text es null .

Posibles micro optimizaciones para experimentar con:

  • ¿Es foreach más rápido o más lento que usar un bucle for como el de abajo? Tenga en cuenta que con el ciclo for puede eliminar la prueba " if (text.Length==0) " al inicio.

    for (int i = 0; i < text.Length; i++) { char c = text[i]; // ...

  • Lo mismo que arriba, pero alzando la llamada de Length . Tenga en cuenta que esto no es bueno para las matrices normales, pero podría ser útil para las cadenas. No lo he probado.

    int length = text.Length; for (int i = 0; i < length; i++) { char c = text[i];

  • En el cuerpo del ciclo, ¿hay alguna diferencia (en velocidad) entre lo que tenemos y:

    if (c != '' '' && c != ''/t'' && c != ''/r'' && c != ''/n'') { return false; }

  • ¿Un interruptor / caja sería más rápido?

    switch (c) { case '' '': case ''/r'': case ''/n'': case ''/t'': return false; }

Actualización sobre el comportamiento Trim

Acabo de ver cómo Trim puede ser tan eficiente como esto. Parece que Trim solo creará una nueva cadena si es necesario. Si puede devolver this o "" :

using System; class Test { static void Main() { CheckTrim(string.Copy("")); CheckTrim(" "); CheckTrim(" x "); CheckTrim("xx"); } static void CheckTrim(string text) { string trimmed = text.Trim(); Console.WriteLine ("Text: ''{0}''", text); Console.WriteLine ("Trimmed ref == text? {0}", object.ReferenceEquals(text, trimmed)); Console.WriteLine ("Trimmed ref == /"/"? {0}", object.ReferenceEquals("", trimmed)); Console.WriteLine(); } }

Esto significa que es muy importante que cualquier punto de referencia en esta pregunta utilice una combinación de datos:

  • Cuerda vacía
  • Espacio en blanco
  • Espacio en blanco alrededor del texto
  • Texto sin espacio en blanco

Por supuesto, el equilibrio del "mundo real" entre estos cuatro es imposible de predecir ...

Puntos de referencia He realizado algunos puntos de referencia de las sugerencias originales en comparación con las mías, y la mía parece ganar en todo lo que le lanzo, lo que me sorprende dados los resultados en otras respuestas. Sin embargo, también comparé la diferencia entre foreach , for usar text.Length , for usar text.Length una vez y luego invirtiendo el orden de iteración, y for con una longitud alzada.

Básicamente, el bucle for es muy ligeramente más rápido, pero al levantar la verificación de longitud es más lento que foreach . Invertir la dirección de bucle for es muy levemente más lento que foreach también. Sospecho fuertemente que el JIT está haciendo cosas interesantes aquí, en términos de eliminar cheques de límites duplicados, etc.

Código: (ver mi entrada de blog de benchmarking para el marco en el que está escrito)

using System; using BenchmarkHelper; public class TrimStrings { static void Main() { Test(""); Test(" "); Test(" x "); Test("x"); Test(new string(''x'', 1000)); Test(" " + new string(''x'', 1000) + " "); Test(new string('' '', 1000)); } static void Test(string text) { bool expectedResult = text.Trim().Length == 0; string title = string.Format("Length={0}, result={1}", text.Length, expectedResult); var results = TestSuite.Create(title, text, expectedResult) /* .Add(x => x.Trim().Length == 0, "Trim().Length == 0") .Add(x => x.Trim() == "", "Trim() == /"/"") .Add(x => x.Trim().Equals(""), "Trim().Equals(/"/")") .Add(x => x.Trim() == string.Empty, "Trim() == string.Empty") .Add(x => x.Trim().Equals(string.Empty), "Trim().Equals(string.Empty)") */ .Add(OriginalIsEmptyOrWhitespace) .Add(IsEmptyOrWhitespaceForLoop) .Add(IsEmptyOrWhitespaceForLoopReversed) .Add(IsEmptyOrWhitespaceForLoopHoistedLength) .RunTests() .ScaleByBest(ScalingMode.VaryDuration); results.Display(ResultColumns.NameAndDuration | ResultColumns.Score, results.FindBest()); } public static bool OriginalIsEmptyOrWhitespace(string text) { if (text.Length == 0) { return true; } foreach (char c in text) { if (c=='' '' || c==''/t'' || c==''/r'' || c==''/n'') { continue; } return false; } return true; } public static bool IsEmptyOrWhitespaceForLoop(string text) { for (int i=0; i < text.Length; i++) { char c = text[i]; if (c=='' '' || c==''/t'' || c==''/r'' || c==''/n'') { continue; } return false; } return true; } public static bool IsEmptyOrWhitespaceForLoopReversed(string text) { for (int i=text.Length-1; i >= 0; i--) { char c = text[i]; if (c=='' '' || c==''/t'' || c==''/r'' || c==''/n'') { continue; } return false; } return true; } public static bool IsEmptyOrWhitespaceForLoopHoistedLength(string text) { int length = text.Length; for (int i=0; i < length; i++) { char c = text[i]; if (c=='' '' || c==''/t'' || c==''/r'' || c==''/n'') { continue; } return false; } return true; } }

Resultados:

============ Length=0, result=True ============ OriginalIsEmptyOrWhitespace 30.012 1.00 IsEmptyOrWhitespaceForLoop 30.802 1.03 IsEmptyOrWhitespaceForLoopReversed 32.944 1.10 IsEmptyOrWhitespaceForLoopHoistedLength 35.113 1.17 ============ Length=1, result=True ============ OriginalIsEmptyOrWhitespace 31.150 1.04 IsEmptyOrWhitespaceForLoop 30.051 1.00 IsEmptyOrWhitespaceForLoopReversed 31.602 1.05 IsEmptyOrWhitespaceForLoopHoistedLength 33.383 1.11 ============ Length=3, result=False ============ OriginalIsEmptyOrWhitespace 30.221 1.00 IsEmptyOrWhitespaceForLoop 30.131 1.00 IsEmptyOrWhitespaceForLoopReversed 34.502 1.15 IsEmptyOrWhitespaceForLoopHoistedLength 35.690 1.18 ============ Length=1, result=False ============ OriginalIsEmptyOrWhitespace 31.626 1.05 IsEmptyOrWhitespaceForLoop 30.005 1.00 IsEmptyOrWhitespaceForLoopReversed 32.383 1.08 IsEmptyOrWhitespaceForLoopHoistedLength 33.666 1.12 ============ Length=1000, result=False ============ OriginalIsEmptyOrWhitespace 30.177 1.00 IsEmptyOrWhitespaceForLoop 33.207 1.10 IsEmptyOrWhitespaceForLoopReversed 30.867 1.02 IsEmptyOrWhitespaceForLoopHoistedLength 31.837 1.06 ============ Length=1002, result=False ============ OriginalIsEmptyOrWhitespace 30.217 1.01 IsEmptyOrWhitespaceForLoop 30.026 1.00 IsEmptyOrWhitespaceForLoopReversed 34.162 1.14 IsEmptyOrWhitespaceForLoopHoistedLength 34.860 1.16 ============ Length=1000, result=True ============ OriginalIsEmptyOrWhitespace 30.303 1.01 IsEmptyOrWhitespaceForLoop 30.018 1.00 IsEmptyOrWhitespaceForLoopReversed 35.475 1.18 IsEmptyOrWhitespaceForLoopHoistedLength 40.927 1.36


Realmente no sé cuál es más rápido; aunque mi instinto me dice que es el número uno. Pero aquí hay otro método:

if (String.IsNullOrEmpty(myString.Trim()))


Verificar la longitud de una cadena por ser cero es la forma más eficiente de probar una cadena vacía, así que diría el número 1:

if (myString.Trim().Length == 0)

La única forma de optimizar esto podría ser evitar el recorte mediante el uso de una expresión regular compilada (Editar: esto es mucho más lento que usar Trim (). Longitud).

Editar: La sugerencia de usar Length proviene de una guía de FxCop. También lo probé: es 2-3 veces más rápido que compararlo con una cadena vacía. Sin embargo, ambos enfoques son extremadamente rápidos (estamos hablando de nanosegundos), así que no importa cuál uses. Recortar es mucho más un cuello de botella, es cientos de veces más lento que la comparación real al final.


Editar: Nuevas pruebas:

Test orders: x. Test name Ticks: xxxxx //Empty String Ticks: xxxxx //two space Ticks: xxxxx //single letter Ticks: xxxxx //single letter with space Ticks: xxxxx //long string Ticks: xxxxx //long string with space 1. if (myString.Trim().Length == 0) ticks: 4121800 ticks: 7523992 ticks: 17655496 ticks: 29312608 ticks: 17302880 ticks: 38160224 2. if (myString.Trim() == "") ticks: 4862312 ticks: 8436560 ticks: 21833776 ticks: 32822200 ticks: 21655224 ticks: 42358016 3. if (myString.Trim().Equals("")) ticks: 5358744 ticks: 9336728 ticks: 18807512 ticks: 30340392 ticks: 18598608 ticks: 39978008 4. if (myString.Trim() == String.Empty) ticks: 4848368 ticks: 8306312 ticks: 21552736 ticks: 32081168 ticks: 21486048 ticks: 41667608 5. if (myString.Trim().Equals(String.Empty)) ticks: 5372720 ticks: 9263696 ticks: 18677728 ticks: 29634320 ticks: 18551904 ticks: 40183768 6. if (IsEmptyOrWhitespace(myString)) //See John Skeet''s Post for algorithm ticks: 6597776 ticks: 9988304 ticks: 7855664 ticks: 7826296 ticks: 7885200 ticks: 7872776 7. is (string.IsNullOrEmpty(myString.Trim()) //Cloud''s suggestion ticks: 4302232 ticks: 10200344 ticks: 18425416 ticks: 29490544 ticks: 17800136 ticks: 38161368

Y el código utilizado:

public void Main() { string res = string.Empty; for (int j = 0; j <= 5; j++) { string myString = ""; switch (j) { case 0: myString = ""; break; case 1: myString = " "; break; case 2: myString = "x"; break; case 3: myString = "x "; break; case 4: myString = "this is a long string for testing triming empty things."; break; case 5: myString = "this is a long string for testing triming empty things. "; break; } bool result = false; Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i <= 100000; i++) { result = myString.Trim().Length == 0; } sw.Stop(); res += "ticks: " + sw.ElapsedTicks + Environment.NewLine; } Console.ReadKey(); //break point here to get the results }


myString.Trim (). Length == 0 Tomó: 421 ms

myString.Trim () == '''' tomó: 468 ms

if (myString.Trim (). Equals ("") Tomó: 515 ms

if (myString.Trim () == String.Empty) Tomó: 484 ms

if (myString.Trim (). Equals (String.Empty)) Tomó: 500 ms

if (string.IsNullOrEmpty (myString.Trim ())) Tomó: 437 ms

En mis pruebas, parece myString.Trim (). Length == 0 y, sorprendentemente, string.IsNullOrEmpty (myString.Trim ()) fueron consistentemente los más rápidos. Los resultados anteriores son un resultado típico de hacer 10,000,000 comparaciones.


public static bool IsNullOrEmpty(this String str, bool checkTrimmed) { var b = String.IsNullOrEmpty(str); return checkTrimmed ? b && str.Trim().Length == 0 : b; }



Como acabo de comenzar, no puedo comentar, así que aquí está.

if (String.IsNullOrEmpty(myString.Trim()))

Trim() llamada Trim() fallará si myString es nulo, ya que no puede invocar métodos en un objeto que es nulo ( NullReferenceException ).

Entonces, la sintaxis correcta sería algo como esto:

if (!String.IsNullOrEmpty(myString)) { string trimmedString = myString.Trim(); //do the rest of you code } else { //string is null or empty, don''t bother processing it }