合并主线最近几年来的主要更新,重点推进TinyHttpClient,替代HttpClient
大石头 authored at 2023-03-08 18:39:12
7.36 KiB
X_NET20
using System;
using System.Reflection;
using System.Security.Cryptography;
using NewLife.Collections;
using NewLife.Reflection;

namespace NewLife.Security;

/// <summary>随机数</summary>
public static class Rand
{
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
    /// <summary>返回一个小于所指定最大值的非负随机数</summary>
    /// <param name="max">返回的随机数的上界(随机数不能取该上界值)</param>
    /// <returns></returns>
    public static Int32 Next(Int32 max = Int32.MaxValue) => RandomNumberGenerator.GetInt32(max);

    /// <summary>返回一个指定范围内的随机数</summary>
    /// <remarks>
    /// 调用平均耗时37.76ns,其中GC耗时77.56%
    /// </remarks>
    /// <param name="min">返回的随机数的下界(随机数可取该下界值)</param>
    /// <param name="max">返回的随机数的上界(随机数不能取该上界值)</param>
    /// <returns></returns>
    public static Int32 Next(Int32 min, Int32 max) => RandomNumberGenerator.GetInt32(min, max);
#else
    private static readonly RandomNumberGenerator _rnd;

    static Rand() => _rnd = new RNGCryptoServiceProvider();

    /// <summary>返回一个小于所指定最大值的非负随机数</summary>
    /// <param name="max">返回的随机数的上界(随机数不能取该上界值)</param>
    /// <returns></returns>
    public static Int32 Next(Int32 max = Int32.MaxValue)
    {
        if (max <= 0) throw new ArgumentOutOfRangeException(nameof(max));

        return Next(0, max);
    }

    [ThreadStatic]
    private static Byte[] _buf;
    /// <summary>返回一个指定范围内的随机数</summary>
    /// <remarks>
    /// 调用平均耗时37.76ns,其中GC耗时77.56%
    /// </remarks>
    /// <param name="min">返回的随机数的下界(随机数可取该下界值)</param>
    /// <param name="max">返回的随机数的上界(随机数不能取该上界值)</param>
    /// <returns></returns>
    public static Int32 Next(Int32 min, Int32 max)
    {
        if (max <= min) throw new ArgumentOutOfRangeException(nameof(max));

        if (_buf == null) _buf = new Byte[4];
        _rnd.GetBytes(_buf);

        var n = BitConverter.ToInt32(_buf, 0);
        if (min == Int32.MinValue && max == Int32.MaxValue) return n;
        if (min == 0 && max == Int32.MaxValue) return Math.Abs(n);
        if (min == Int32.MinValue && max == 0) return -Math.Abs(n);

        var num = max - min;
        // 不要进行复杂运算,看做是生成从0到(max-min)的随机数,然后再加上min即可
        return (Int32)((num * (UInt32)n >> 32) + min);
    }
#endif

    /// <summary>返回指定长度随机字节数组</summary>
    /// <remarks>
    /// 调用平均耗时5.46ns,其中GC耗时15%
    /// </remarks>
    /// <param name="count"></param>
    /// <returns></returns>
    public static Byte[] NextBytes(Int32 count)
    {
#if NET6_0_OR_GREATER
        return RandomNumberGenerator.GetBytes(count);
#elif NETFRAMEWORK || NETSTANDARD2_0
        return new Random().NextBytes(count);
#else
        var buf = new Byte[count];
        RandomNumberGenerator.Fill(buf);
        return buf;
#endif
    }

#if NETFRAMEWORK || NETSTANDARD2_0
    /// <summary>
    /// 返回随机数填充的指定长度的数组
    /// </summary>
    /// <param name="random"></param>
    /// <param name="length">数组长度</param>
    /// <returns>随机数填充的指定长度的数组</returns>
    private static byte[] NextBytes(this Random random, int length)
    {
        if (length < 0)
        {
            throw new ArgumentOutOfRangeException("length");
        }
        byte[] data = new byte[length];
        random.NextBytes(data);
        return data;
    }
#endif

    /// <summary>返回指定长度随机字符串</summary>
    /// <param name="length">长度</param>
    /// <param name="symbol">是否包含符号</param>
    /// <returns></returns>
    public static String NextString(Int32 length, Boolean symbol = false)
    {
        var sb = Pool.StringBuilder.Get();
        for (var i = 0; i < length; i++)
        {
            var ch = ' ';
            if (symbol)
                ch = (Char)Next(' ', 0x7F);
            else
            {
                var n = Next(0, 10 + 26 + 26);
                if (n < 10)
                    ch = (Char)('0' + n);
                else if (n < 10 + 26)
                    ch = (Char)('A' + n - 10);
                else
                    ch = (Char)('a' + n - 10 - 26);
            }
            sb.Append(ch);
        }

        return sb.Put(true);
    }

    /// <summary>随机填充指定对象的属性。可用于构造随机数据进行测试</summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <returns></returns>
    public static T Fill<T>(T value)
    {
        if (value == null) return value;

        foreach (var pi in value.GetType().GetProperties(true))
        {
            switch (pi.PropertyType.GetTypeCode())
            {
                case TypeCode.Empty:
                case TypeCode.Object:
                case TypeCode.DBNull:
                    break;
                case TypeCode.Boolean:
                    pi.SetValue(value, Next(2) > 0, null);
                    break;
                case TypeCode.Char:
                    pi.SetValue(value, (Char)Next(0, 256), null);
                    break;
                case TypeCode.SByte:
                    pi.SetValue(value, (SByte)Next(-128, 127), null);
                    break;
                case TypeCode.Byte:
                    pi.SetValue(value, (Byte)Next(0, 256), null);
                    break;
                case TypeCode.Int16:
                    pi.SetValue(value, (Int16)Next(Int16.MinValue, Int16.MaxValue), null);
                    break;
                case TypeCode.UInt16:
                    pi.SetValue(value, (UInt16)Next(0, UInt16.MaxValue), null);
                    break;
                case TypeCode.Int32:
                    pi.SetValue(value, Next(), null);
                    break;
                case TypeCode.UInt32:
                    pi.SetValue(value, (UInt32)Next(), null);
                    break;
                case TypeCode.Int64:
                    pi.SetValue(value, (Int64)Next() * Next(), null);
                    break;
                case TypeCode.UInt64:
                    pi.SetValue(value, (UInt64)Next() * (UInt64)Next(), null);
                    break;
                case TypeCode.Single:
                    pi.SetValue(value, Next() / 100f, null);
                    break;
                case TypeCode.Double:
                    pi.SetValue(value, Next() / 10000d, null);
                    break;
                case TypeCode.Decimal:
                    pi.SetValue(value, (Decimal)Next() / 10000, null);
                    break;
                case TypeCode.DateTime:
                    pi.SetValue(value, new DateTime(2000, 1, 1).AddSeconds(Next(20 * 365 * 24 * 3600)), null);
                    break;
                case TypeCode.String:
                    pi.SetValue(value, NextString(8), null);
                    break;
                default:
                    break;
            }
        }

        return value;
    }
}