2011-01-25 7 views
6

Tak, napisałem małą i, jak początkowo sądziłem, łatwą metodę w języku C#. Ta metoda statyczna ma być używany jako prosty generator haseł sugestii, a kod wygląda następująco:Moja statyczna funkcja C# polega na graniu ze mną w gry ... totalnie dziwne!

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

nazywam tę funkcję tak:

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

Teraz ta metoda prawie zawsze zwracaj losowe ciągi, takie jak "AAAAAA", "BBBBBB", "888888" itd., gdzie myślę, że powinien zwrócić ciągi takie jak "A8JK2A", "82mOK7" itp.

Jednak i tu jest część dziwna; Jeśli wstawię punkt przerwania i przejdę przez tę linię iteracji po linii, otrzymam w zamian prawidłowy typ hasła. W 100% innych przypadków, gdy nie debuguję, daje mi to bzdura jak "AAAAAA", "666666", itd.

Jak to jest możliwe? Wszelkie sugestie są mile widziane! :-)

BTW, mój system: Visual Studio 2010, C# 4.0, ASP.NET MVC 3 RTM projektu w/ASP.NET Development Server. Nie testowałem tego kodu w żadnym innym środowisku.

+0

Nie wiem, czy to powoduje Twój problem, ale prawdopodobnie nie chcesz tworzyć nowej instancji losowej w każdej iteracji. Powinieneś to zrobić raz przed pętlą. Domyślny konstruktor Random używa Environment.TickCount jako materiału siewnego. Ponadto domyślna wartość dla źródła nie ma większego sensu, ponieważ myślę, że pusty ciąg spowoduje niepowodzenie wywołania Substring. –

+0

Masz rację, panie Putty! Początkowo umieszczono tam ciąg "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890". Dzięki! – CoderBang

Odpowiedz

15

Przenieś deklarację dla randomObj poza pętlę. Podczas debugowania tworzy on za każdym razem nowe ziarno, ponieważ różnica nasion jest wystarczająca. Ale gdy nie debugujesz, czas wysiewu jest w zasadzie taki sam dla każdej iteracji pętli, więc daje za każdym razem tę samą wartość początkową.

I niewielka nitka - dobrze jest używać StringBuilder zamiast stringów do tego typu rzeczy, więc nie musisz ponownie inicjować miejsca w pamięci za każdym razem, gdy dodajesz znak do ciągu znaków .

Innymi słowy, podobnie jak to:

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, to wyjaśnia! Wielkie dzięki, Ken! :) – CoderBang

+0

Osobiście poszedłbym na char [] (zamiast StringBuilder) i zaczepił char od źródła za pośrednictwem indeksu, ale całkiem podobnie. –

+2

+1. Kolejna uwaga, prawie zawsze lepiej jest zadeklarować losowy obiekt w jak najmniejszym stopniu. Posunąłbym się nawet do wyodrębnienia deklaracji w statyczne pole. Jeśli twoje dwa wątki trafiałyby w tę funkcję w tym samym czasie, istnieje szansa, że ​​wypiszesz te same hasła. – Rob

4

Zachowanie widzisz dlatego Random jest podstawie czasu, a kiedy nie jesteś debugowania leci przez wszystkich 5 powtórzeń w tym samym moment (mniej więcej). Więc pytasz o pierwszą losową liczbę z tego samego nasienia. Podczas debugowania wystarcza wystarczająco dużo czasu, aby za każdym razem uzyskać nowe ziarno.

Move deklarację Random zewnątrz pętli:

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

Teraz jesteś posuwa się naprzód 5 kroków od losowych zamiast poruszającego 1 krok od samego losowych 5 razy .

2

Tworzysz instancję Random() w każdej iteracji za pośrednictwem pętli z nowym seedem zależnym od czasu. Biorąc pod uwagę ziarnistość zegara systemowego i szybkość współczesnych procesorów, gwarantuje to, że ponownie uruchamiasz sekwencję pseudolosową z tym samym nasieniem.

Spróbuj coś jak poniżej, chociaż jeśli jesteś jednowątkowy można bezpiecznie ominąć blokadę():

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

Wystarczy tylko instancję RNG raz. Każdy losuje bity z tego samego RNG, więc powinien zachowywać się bardziej jak źródło entropii.Jeśli potrzebujesz powtarzalności do testowania, użyj przeciążenia konstruktora losowego, które pozwala dostarczyć ziarno. To samo nasienie == ta sama sekwencja pseudolosowa.

Powiązane problemy