/// <summary> /// Constructor. /// </summary> /// <param name="defaultLength">Default length for generated values. /// This is only used when <see cref="GetRandomString"/> has an input length /// of 0.</param> /// <param name="characterSet">The character set to base the random generation off of. /// You MAY have duplicate chars, which is useful to increase the frequency of those characters.</param> /// <param name="symbolsToAdd">We've made this a separate parameter, because in most users /// cases, they want the default ascii alpha-numeric chars (A-Z, a-z, 0-9), but usually /// would want their own customized set of which symbols to use. This way, you don't have to /// send in a custom char set every time just to set which symbols you want to set. Note: /// if this is set, <paramref name="addDefaultSymbolSet"/> CANNOT be set to true.</param> /// <param name="addDefaultSymbolSet">True to add the default symbol set (see static /// property: <see cref="SymbolCharsDef"/>, which is not readonly, so you may set your own /// default for this globally if desired). Do NOT set this to true if you send in your own /// <paramref name="symbolsToAdd"/>.</param> /// <param name="enforceDistinctCharSet">This will filter the input char set with a Distinct /// operation, which will (without error) remove any (presumably accidental) duplicates. /// This is NOT run by default, because you may *want* to increase a chars probability by /// repeating it n number of times in the input char set.</param> /// <param name="bufferLength">Sets the buffer size in bytes of the internal <see cref="CryptoRandom"/> /// instance. Increase this value to a kilobyte or greater in order to get extremely good performance. /// This makes most sense when reusing this instance more than once.</param> public RandomStringGenerator( int defaultLength = 0, string characterSet = null, string symbolsToAdd = null, bool addDefaultSymbolSet = false, bool enforceDistinctCharSet = false, int bufferLength = 512) { if (defaultLength > 0) { DefaultLength = defaultLength.Max(0); } CharacterSet = characterSet ?? CharSetDef; if (addDefaultSymbolSet) { if (symbolsToAdd.NotNulle()) { throw new ArgumentException($"Argument '{nameof(addDefaultSymbolSet)}' cannot be true while '{nameof(symbolsToAdd)}' is set (not null)."); } symbolsToAdd = SymbolCharsDef; } if (symbolsToAdd.NotNulle()) { CharacterSet += symbolsToAdd; } // need this HashSet because its hard to detect if a char is actually a symbol later // (char.IsSymbol is deficient, so just counting as 'symbol' any non letterOrDigit char[] symbols = CharacterSet.Where(c => !char.IsLetterOrDigit(c)).ToArray(); if (symbols.NotNulle()) { SymbolsHashset = new HashSet <char>(symbols.Distinct()); SymbolSet = new string(symbols); } _charSet = enforceDistinctCharSet ? CharacterSet.Distinct().ToArray() : CharacterSet.ToArray(); if (_charSet.IsNulle()) { throw new ArgumentOutOfRangeException("No characters were set.", nameof(CharacterSet)); } bufferLength = (CharacterSet.Length * 2).Max(bufferLength); // gets bigger of two, doesn't let input be smaller than charSet*2 bufferLength = bufferLength.MinMax(256, 1024 * 4); // 4 kb max, 1/2 a byte min, for this max, no need to generate huge buffer value, perf is regardless once your getting > 4 kb _cryptoRandom = new CryptoRandom(bufferLength); // If true, before generating the random string, we will radomize the source char set // as well, making for a doubly randomized result. TRUE by default. bool randomizeCharSet = true; if (randomizeCharSet) { _charSet = _cryptoRandom.RandomShuffle(_charSet); bool viewDiagnostic = false; if (viewDiagnostic) { //// just to see none of the original chars were lost in that sort: string shuffledStr = new string(_charSet); string sortedStr = _charSet.OrderBy(c => c).JoinToString(""); // view to witness no chars were dropped (or added) } } }