2011-01-25 20 views
6

Entonces, he escrito un método pequeño y, por lo que inicialmente pensé, fácil en C#. Este método estático está destinado a ser utilizado como un simple generador de contraseñas sugerencia, y el código es el siguiente:Mi función C# estática está jugando juegos conmigo ... ¡Totalmente raro!

public static string CreateRandomPassword(int outputLength, string source = "") 
{ 
    var output = string.Empty; 

    for (var i = 0; i < outputLength; i++) 
    { 
    var randomObj = new Random(); 
    output += source.Substring(randomObj.Next(source.Length), 1); 
    } 

    return output; 
} 

que llamo esta función como esta:

var randomPassword = StringHelper.CreateRandomPassword(5, "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); 

Ahora, este método casi siempre devolver cadenas aleatorias como "AAAAAA", "BBBBBB", "888888" etc., donde pensé que debería devolver cadenas como "A8JK2A", "82mOK7", etc.

Sin embargo, y aquí está la parte más extraña; Si coloco un punto de interrupción y paso por esta iteración línea por línea, obtengo el tipo correcto de contraseña a cambio. En el 100% de los otros casos, cuando no estoy depurando, me da basura como "AAAAAA", "666666", etc.

¿Cómo es esto posible? ¡Cualquier sugerencia sera grandemente apreciada! :-)

BTW, mi sistema: Visual Studio 2010, C# 4.0, proyecto ASP.NET MVC 3 RTM con ASP.NET Development Server. No he probado este código en ningún otro entorno.

+0

No sé si está causando su problema, pero probablemente no desee crear una nueva instancia de Aleatorio en cada iteración. Deberías hacer esto una vez antes del ciclo. El constructor predeterminado de Random usa Environment.TickCount como una semilla. Además, su valor predeterminado para la fuente no tiene mucho sentido ya que creo que la cadena vacía causará un error en la llamada a la subcadena. –

+0

¡Tiene razón, Sr. Putty! Inicialmente tenía la cadena "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" colocada allí. ¡Gracias! – CoderBang

Respuesta

15

Mueva la declaración para el randomObj fuera del bucle. Cuando lo depura, lo crea con una nueva semilla cada vez, porque hay suficiente diferencia de tiempo para que la semilla sea diferente. Pero cuando no está depurando, el tiempo de inicialización es básicamente el mismo para cada iteración del ciclo, por lo que le proporciona el mismo valor de inicio cada vez.

Y un detalle menor: es un buen hábito utilizar un StringBuilder en lugar de una cadena para este tipo de cosas, por lo que no tiene que reinicializar el espacio de memoria cada vez que agrega un carácter a la cadena .

En otras palabras, de esta manera:

public static string CreateRandomPassword(int outputLength, string source = "") 
{ 
    var output = new StringBuilder(); 
    var randomObj = new Random(); 
    for (var i = 0; i < outputLength; i++) 
    { 
    output.Append(source.Substring(randomObj.Next(source.Length), 1)); 
    } 
    return output.ToString(); 
} 
+0

¡Ah, eso lo explica! Muchas gracias Ken! :) – CoderBang

+0

Personalmente me hubiera ido por un char [] (en lugar de StringBuilder) y enganchar el char de la fuente a través del indexador, pero bastante similar. –

+2

+1. Solo otra nota, casi siempre es mejor declarar el objeto aleatorio lo menos posible. Incluso llegaría a extraer la declaración en un campo estático. Si tiene dos hilos al presionar esta función al mismo tiempo, existe la posibilidad de que escuche exactamente las mismas contraseñas. – Rob

4

El comportamiento que se está viendo es porque Random es , y cuando usted no está de depuración que vuela a través de los 5 iteraciones al mismo tiempo basa momento (más o menos). Entonces, estás pidiendo el primer número aleatorio de la misma semilla. Cuando está depurando, toma suficiente tiempo obtener una nueva semilla cada vez.

mover la declaración de Random fuera del bucle:

var randomObj = new Random(); 
for (var i = 0; i < outputLength; i++) 
{ 
    output += source.Substring(randomObj.Next(source.Length), 1); 
} 

Ahora estás avanzar 5 pasos de una semilla aleatoria en lugar de mover 1 paso de la misma semilla aleatoria 5 veces .

2

Está instanciando una nueva instancia de Random() en cada iteración a través del ciclo con una nueva inicialización dependiente del tiempo. Dada la granularidad del reloj del sistema y la velocidad de las CPU modernas, esto garantiza que reinicie la secuencia pseudoaleatoria una y otra vez con la misma semilla.

Prueba algo como lo siguiente, aunque si eres de un solo subproceso, se puede omitir con seguridad el bloqueo():

private static Random randomBits = new Random() ; 
public static string CreateRandomPassword(int outputLength, string source = "") 
{ 
    StringBuilder sb = new StringBuilder(outputLength) ; 
    lock (randomBits) 
    { 
    while (sb.Length < outputLength) 
    { 
     sb.Append(randomBits.Next(source.Length) , 1) ; 
    } 
    } 
    return sb.ToString() ; 
} 

Sólo una instancia del generador de números aleatorios de una vez. Cada uno extrae bits del mismo generador de números aleatorios, por lo que debe comportarse mucho más como una fuente de entropía.Si necesita repetibilidad para las pruebas, use la sobrecarga del constructor Random que le permite suministrar la semilla. La misma semilla == misma secuencia pseudoaleatoria.

Cuestiones relacionadas