Pergunta

Então, eu escrevi um pequeno método fácil e, pelo que inicialmente pensei, em C#. Este método estático deve ser usado como um gerador de sugestão de senha simples, e o código se parece com o seguinte:

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

Eu chamo essa função assim:

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

Agora, esse método quase sempre retorna seqüências aleatórias como "aaaaaaa", "bbbbbb", "888888" etc., onde eu pensei que deveria devolver strings como "A8JK2a", "82mok7" etc.

No entanto, e aqui está a parte estranha; Se eu colocar um ponto de interrupção e passar por essa linha de iteração por linha, recebo o tipo correto de senha em troca. Em 100% dos outros casos, quando não estou depurando, isso me dá uma porcaria como "aaaaaa", "666666", etc ..

Como isso é possível? Qualquer sugestão é muito apreciada! :-)

BTW, meu sistema: Visual Studio 2010, C# 4.0, ASP.NET MVC 3 RTM Project w/ Asp.Net Development Server. Não testei este código em nenhum outro ambiente.

Foi útil?

Solução

Mova a declaração para o Randomobj fora do loop. Quando você está depurando, ele cria uma nova semente a cada vez, porque há diferença de tempo suficiente para que a semente seja diferente. Mas quando você não está depurando, o tempo de semente é basicamente o mesmo para cada iteração do loop, por isso está fornecendo o mesmo valor de início a cada vez.

E um NIT menor-é um bom hábito usar um StringBuilder em vez de uma string para esse tipo de coisa, para que você não precise reinicializar o espaço de memória toda vez que anexar um personagem à string.

Em outras palavras, assim:

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

Outras dicas

O comportamento que você está vendo é porque Random é baseado no tempo, e quando você não está depurando, ele voa por todas as 5 iterações no mesmo momento (mais ou menos). Então você está pedindo o primeiro número aleatório da mesma semente. Quando você está depurando, leva o tempo suficiente para obter uma nova semente a cada vez.

Mover a declaração de Random Fora do loop:

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

Agora você está Avançando a 5 passos de uma semente aleatória ao invés de movendo -se a um passo da mesma semente aleatória 5 vezes.

Você está instanciando uma nova instância de aleatoriamente () em cada iteração através do loop com uma nova semente dependente do tempo. Dada a granularidade do relógio do sistema e a velocidade das CPUs modernas, isso garante que você reinicie a sequência pseudo-aleatória repetidamente com a mesma semente.

Experimente algo como o seguinte, embora se você for um thread único, pode omitir com segurança o bloqueio ():

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

Você apenas instancia o RNG uma vez. Todo mundo desenha bits do mesmo RNG, por isso deve se comportar muito mais como uma fonte de entropia. Se você precisar de repetibilidade para teste, use a sobrecarga de construtor aleatório que permite fornecer a semente. Mesma semente == mesma sequência pseudo-aleatória.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top