-
Notifications
You must be signed in to change notification settings - Fork 0
Fix #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix #1
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,28 +1,61 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFrameworks>net8.0;net6.0;netstandard2.0;net462</TargetFrameworks> | ||
| <Nullable>enable</Nullable> | ||
| <!-- Target multiple frameworks --> | ||
| <TargetFrameworks>net8.0;net7.0;net6.0;netstandard2.0;net48;net472;net462</TargetFrameworks> | ||
|
|
||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||
| <Nullable>enable</Nullable> | ||
| <LangVersion>10.0</LangVersion> | ||
|
|
||
| <!-- Package info --> | ||
| <PackageId>DryFish.ILib.Random</PackageId> | ||
| <Version>2026.1.0</Version> | ||
| <Authors>DryFish</Authors> | ||
| <Description>Random generation utilities for DryFish.ILib</Description> | ||
| <PackageTags>random;utility;dryfish;ilib</PackageTags> | ||
| <PackageLicenseExpression>MIT</PackageLicenseExpression> | ||
| <Company>DryFish</Company> | ||
| <Description>Random generation utilities for DryFish.ILib - strings, numbers, characters, colors, dates and more. Supports .NET 6/7/8, .NET Standard 2.0, and .NET Framework 4.6.2+.</Description> | ||
| <PackageTags>random;utility;generation;dryfish;ilib;dotnet;cross-platform;randomizer</PackageTags> | ||
| <RepositoryUrl>https://github.com/dryfish09/ILib.Random</RepositoryUrl> | ||
| <PackageLicenseExpression>MIT</PackageLicenseExpression> | ||
|
|
||
| <!-- README for NuGet --> | ||
| <PackageReadmeFile>README.md</PackageReadmeFile> | ||
|
|
||
| <!-- Generate documentation --> | ||
| <GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
|
|
||
| <!-- Symbols for debugging --> | ||
| <IncludeSymbols>true</IncludeSymbols> | ||
| <SymbolPackageFormat>snupkg</SymbolPackageFormat> | ||
|
|
||
| <!-- Output --> | ||
| <GeneratePackageOnBuild>false</GeneratePackageOnBuild> | ||
| <PackageOutputPath>../nupkg</PackageOutputPath> | ||
| </PropertyGroup> | ||
|
|
||
| <!-- Chỉ enable nullable cho .NET 6/8 --> | ||
| <PropertyGroup Condition="'$(TargetFramework)' == 'net6.0' OR '$(TargetFramework)' == 'net8.0'"> | ||
| <Nullable>enable</Nullable> | ||
| <!-- .NET 6+ specific features --> | ||
| <PropertyGroup Condition="'$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net7.0' Or '$(TargetFramework)' == 'net6.0'"> | ||
| <DefineConstants>$(DefineConstants);NET6_0_OR_GREATER</DefineConstants> | ||
| </PropertyGroup> | ||
| <!-- Disable nullable cho .NET Standard và .NET Framework --> | ||
| <PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'net462'"> | ||
| <Nullable>disable</Nullable> | ||
| <LangVersion>8.0</LangVersion> | ||
|
|
||
| <!-- .NET Framework specific --> | ||
| <PropertyGroup Condition="'$(TargetFramework)' == 'net48' Or '$(TargetFramework)' == 'net472' Or '$(TargetFramework)' == 'net462'"> | ||
| <DefineConstants>$(DefineConstants);NETFRAMEWORK</DefineConstants> | ||
| <NoWarn>$(NoWarn);CS8600;CS8602;CS8603;CS8604;CS8618;CS8625</NoWarn> | ||
| </PropertyGroup> | ||
|
|
||
| <!-- .NET Standard --> | ||
| <PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> | ||
| <DefineConstants>$(DefineConstants);NETSTANDARD</DefineConstants> | ||
| </PropertyGroup> | ||
|
|
||
| <!-- Include README and LICENSE in package --> | ||
| <ItemGroup> | ||
| <None Include="../README.md" Pack="true" PackagePath="/"/> | ||
| <None Include="../LICENSE" Pack="true" PackagePath="/"/> | ||
| </ItemGroup> | ||
|
|
||
| <!-- Conditional reference for .NET Framework --> | ||
| <ItemGroup Condition="'$(TargetFramework)' == 'net48' Or '$(TargetFramework)' == 'net472' Or '$(TargetFramework)' == 'net462'"> | ||
| <PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" /> | ||
| </ItemGroup> | ||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,113 +1,169 @@ | ||
| namespace DryFish.ILib.Random; | ||
| using System; | ||
| using System.Collections.Generic; | ||
|
|
||
| public static class ILibRandom | ||
| namespace DryFish.ILib.Random | ||
| { | ||
| private static readonly Random _random = new Random(); | ||
|
|
||
| /// <summary> | ||
| /// Returns a random element from the specified array | ||
| /// Random generation utilities for DryFish.ILib | ||
| /// </summary> | ||
| /// <param name="array">Array of strings</param> | ||
| /// <returns>Random element from array</returns> | ||
| public static string IRandomFromArray(string[] array) | ||
| public static class ILibRandom | ||
| { | ||
| if (array == null || array.Length == 0) | ||
| throw new ArgumentException("Array cannot be null or empty"); | ||
|
|
||
| return array[_random.Next(array.Length)]; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random element from the specified array (generic) | ||
| /// </summary> | ||
| /// <typeparam name="T">Type of array elements</typeparam> | ||
| /// <param name="array">Array of type T</param> | ||
| /// <returns>Random element from array</returns> | ||
| public static T IRandomFromArray<T>(T[] array) | ||
| { | ||
| if (array == null || array.Length == 0) | ||
| throw new ArgumentException("Array cannot be null or empty"); | ||
|
|
||
| return array[_random.Next(array.Length)]; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random integer between min and max (inclusive) | ||
| /// </summary> | ||
| /// <param name="min">Minimum value</param> | ||
| /// <param name="max">Maximum value</param> | ||
| /// <returns>Random integer</returns> | ||
| public static int IRandomInt(int min, int max) => _random.Next(min, max + 1); | ||
|
|
||
| /// <summary> | ||
| /// Returns a random integer between 0 and 100 | ||
| /// </summary> | ||
| public static int IRandomInt() => _random.Next(101); | ||
|
|
||
| /// <summary> | ||
| /// Returns a random character between min and max | ||
| /// </summary> | ||
| /// <param name="min">Minimum character</param> | ||
| /// <param name="max">Maximum character</param> | ||
| /// <returns>Random character</returns> | ||
| public static char IRandomChar(char min, char max) => (char)_random.Next(min, max + 1); | ||
|
|
||
| /// <summary> | ||
| /// Returns a random alphabet character between min and max | ||
| /// </summary> | ||
| /// <param name="min">Minimum alphabet (e.g., 'A')</param> | ||
| /// <param name="max">Maximum alphabet (e.g., 'Z')</param> | ||
| /// <returns>Random alphabet character</returns> | ||
| public static char IRandomAlphabet(char min, char max) | ||
| { | ||
| if (!char.IsLetter(min) || !char.IsLetter(max)) | ||
| throw new ArgumentException("Only alphabet characters allowed"); | ||
|
|
||
| return (char)_random.Next(min, max + 1); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random uppercase letter (A-Z) | ||
| /// </summary> | ||
| public static char IRandomUppercase() => (char)_random.Next('A', 'Z' + 1); | ||
|
|
||
| /// <summary> | ||
| /// Returns a random lowercase letter (a-z) | ||
| /// </summary> | ||
| public static char IRandomLowercase() => (char)_random.Next('a', 'z' + 1); | ||
|
|
||
| /// <summary> | ||
| /// Returns a random boolean value | ||
| /// </summary> | ||
| public static bool IRandomBool() => _random.Next(2) == 1; | ||
|
|
||
| /// <summary> | ||
| /// Returns a random long between min and max | ||
| /// </summary> | ||
| public static long IRandomLong(long min, long max) | ||
| { | ||
| if (min > max) throw new ArgumentException("min must be <= max"); | ||
| long range = max - min; | ||
| long rand = (long)(_random.NextDouble() * (range + 1)); | ||
| return min + rand; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random double between min and max | ||
| /// </summary> | ||
| public static double IRandomDouble(double min = 0.0, double max = 1.0) | ||
| { | ||
| if (min > max) throw new ArgumentException("min must be <= max"); | ||
| return min + (_random.NextDouble() * (max - min)); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random item from a list | ||
| /// </summary> | ||
| public static T IRandomItem<T>(IList<T> list) | ||
| { | ||
| if (list == null || list.Count == 0) | ||
| throw new ArgumentException("List cannot be null or empty"); | ||
| return list[_random.Next(list.Count)]; | ||
| private static readonly System.Random _random = new System.Random(); | ||
|
|
||
| /// <summary> | ||
| /// Returns a random element from the specified array | ||
| /// </summary> | ||
| /// <param name="array">Array of strings</param> | ||
| /// <returns>Random element from array</returns> | ||
| public static string IRandomFromArray(string[] array) | ||
| { | ||
| if (array == null || array.Length == 0) | ||
| throw new ArgumentException("Array cannot be null or empty"); | ||
|
|
||
| return array[_random.Next(array.Length)]; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random element from the specified array (generic) | ||
| /// </summary> | ||
| /// <typeparam name="T">Type of array elements</typeparam> | ||
| /// <param name="array">Array of type T</param> | ||
| /// <returns>Random element from array</returns> | ||
| public static T IRandomFromArray<T>(T[] array) | ||
| { | ||
| if (array == null || array.Length == 0) | ||
| throw new ArgumentException("Array cannot be null or empty"); | ||
|
|
||
| return array[_random.Next(array.Length)]; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random integer between min and max (inclusive) | ||
| /// </summary> | ||
| /// <param name="min">Minimum value</param> | ||
| /// <param name="max">Maximum value</param> | ||
| /// <returns>Random integer</returns> | ||
| public static int IRandomInt(int min, int max) | ||
| { | ||
| return _random.Next(min, max + 1); | ||
| } | ||
|
Comment on lines
+46
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If max is int.MaxValue, max + 1 overflows to int.MinValue, causing an ArgumentOutOfRangeException. Also, there is no validation that min <= max. We can resolve both issues by validating the input and using double arithmetic to calculate the range and offset safely. public static int IRandomInt(int min, int max)
{
if (min > max)
throw new ArgumentException("min must be <= max");
double range = (double)max - (double)min;
int offset = (int)(_random.NextDouble() * (range + 1.0));
return min + offset;
} |
||
|
|
||
| /// <summary> | ||
| /// Returns a random integer between 0 and 100 | ||
| /// </summary> | ||
| public static int IRandomInt() | ||
| { | ||
| return _random.Next(101); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random character between min and max | ||
| /// </summary> | ||
| /// <param name="min">Minimum character</param> | ||
| /// <param name="max">Maximum character</param> | ||
| /// <returns>Random character</returns> | ||
| public static char IRandomChar(char min, char max) | ||
| { | ||
| return (char)_random.Next(min, max + 1); | ||
| } | ||
|
Comment on lines
+65
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If min > max, the method will throw an ArgumentOutOfRangeException from _random.Next. Adding a guard clause to validate that min <= max ensures consistency with other methods and provides a clearer error message. public static char IRandomChar(char min, char max)
{
if (min > max)
throw new ArgumentException("min must be <= max");
return (char)_random.Next(min, max + 1);
} |
||
|
|
||
| /// <summary> | ||
| /// Returns a random alphabet character between min and max | ||
| /// </summary> | ||
| /// <param name="min">Minimum alphabet (e.g., 'A')</param> | ||
| /// <param name="max">Maximum alphabet (e.g., 'Z')</param> | ||
| /// <returns>Random alphabet character</returns> | ||
| public static char IRandomAlphabet(char min, char max) | ||
| { | ||
| if (!char.IsLetter(min) || !char.IsLetter(max)) | ||
| throw new ArgumentException("Only alphabet characters allowed"); | ||
|
|
||
| return (char)_random.Next(min, max + 1); | ||
| } | ||
|
Comment on lines
+76
to
+82
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the range between min and max spans across the gap between uppercase and lowercase letters (e.g., IRandomAlphabet('A', 'z')), the method can return non-letter characters like [, \, ], ^, _, or ` because it simply selects a random character in the range without verifying if the generated character is actually a letter. We should use rejection sampling to ensure only letters are returned, and add a validation check for min > max. public static char IRandomAlphabet(char min, char max)
{
if (min > max)
throw new ArgumentException("min must be <= max");
if (!char.IsLetter(min) || !char.IsLetter(max))
throw new ArgumentException("Only alphabet characters allowed");
char result;
do
{
result = (char)_random.Next(min, max + 1);
} while (!char.IsLetter(result));
return result;
} |
||
|
|
||
| /// <summary> | ||
| /// Returns a random uppercase letter (A-Z) | ||
| /// </summary> | ||
| public static char IRandomUppercase() | ||
| { | ||
| return (char)_random.Next('A', 'Z' + 1); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random lowercase letter (a-z) | ||
| /// </summary> | ||
| public static char IRandomLowercase() | ||
| { | ||
| return (char)_random.Next('a', 'z' + 1); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random boolean value | ||
| /// </summary> | ||
| public static bool IRandomBool() | ||
| { | ||
| return _random.Next(2) == 1; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random long between min and max | ||
| /// </summary> | ||
| public static long IRandomLong(long min, long max) | ||
| { | ||
| if (min > max) throw new ArgumentException("min must be <= max"); | ||
| long range = max - min; | ||
| long rand = (long)(_random.NextDouble() * (range + 1)); | ||
| return min + rand; | ||
| } | ||
|
Comment on lines
+111
to
+117
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. max - min can overflow long if min is negative and max is positive. Additionally, _random.NextDouble() only has 53 bits of precision, which leads to non-uniform distribution and skipped values for ranges larger than 2^53. We can use ulong to safely compute the range without overflow, and use NextBytes to get a full 64-bit random value. public static long IRandomLong(long min, long max)
{
if (min > max) throw new ArgumentException("min must be <= max");
ulong range = (ulong)(max - min);
if (range == ulong.MaxValue)
{
byte[] buf = new byte[8];
_random.NextBytes(buf);
return BitConverter.ToInt64(buf, 0);
}
byte[] bytes = new byte[8];
_random.NextBytes(bytes);
ulong uval = BitConverter.ToUInt64(bytes, 0);
return min + (long)(uval % (range + 1));
} |
||
|
|
||
| /// <summary> | ||
| /// Returns a random double between min and max | ||
| /// </summary> | ||
| public static double IRandomDouble(double min = 0.0, double max = 1.0) | ||
| { | ||
| if (min > max) throw new ArgumentException("min must be <= max"); | ||
| return min + (_random.NextDouble() * (max - min)); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random item from a list | ||
| /// </summary> | ||
| /// <typeparam name="T">Type of list elements</typeparam> | ||
| /// <param name="list">List of type T</param> | ||
| /// <returns>Random item from list</returns> | ||
| public static T IRandomItem<T>(IList<T> list) | ||
| { | ||
| if (list == null || list.Count == 0) | ||
| throw new ArgumentException("List cannot be null or empty"); | ||
| return list[_random.Next(list.Count)]; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random GUID as string | ||
| /// </summary> | ||
| public static string IRandomGuid() | ||
| { | ||
| return System.Guid.NewGuid().ToString(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random hex color code (e.g., #FF5733) | ||
| /// </summary> | ||
| public static string IRandomHexColor() | ||
| { | ||
| return $"#{_random.Next(0x1000000):X6}"; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a random console color name supported by ILib | ||
| /// </summary> | ||
| public static string IRandomConsoleColor() | ||
| { | ||
| string[] colors = { "black", "darkblue", "darkgreen", "darkcyan", "darkred", | ||
| "darkmagenta", "darkyellow", "gray", "grey", "darkgray", | ||
| "darkgrey", "blue", "green", "cyan", "red", "magenta", | ||
| "yellow", "white" }; | ||
| return IRandomFromArray(colors); | ||
| } | ||
|
Comment on lines
+160
to
+167
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The colors array is allocated on every call to IRandomConsoleColor(). Moving it to a private static readonly field avoids unnecessary allocations and improves performance. private static readonly string[] ConsoleColors = {
"black", "darkblue", "darkgreen", "darkcyan", "darkred",
"darkmagenta", "darkyellow", "gray", "grey", "darkgray",
"darkgrey", "blue", "green", "cyan", "red", "magenta",
"yellow", "white"
};
public static string IRandomConsoleColor()
{
return IRandomFromArray(ConsoleColors);
} |
||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
System.Random is not thread-safe. Concurrent access from multiple threads can cause it to enter a state where it always returns 0. Since ILibRandom is a static utility class, it is highly likely to be used in multi-threaded contexts. We can use conditional compilation to use System.Random.Shared on .NET 6+ and [ThreadStatic] on older frameworks.