Pull to refresh

Некоторые простые вещи, которые мигрируют из проекта в проект

Введение


Наверное в каждого большом проекте есть папки с названиями tools, utils, helpers и другими, которые хранят в себе какие-то простые абстракции и статические функции. Часто код из этих папок мигрирует из проекта в проект. Причем в проектах, авторы которых друг-друга в глаза не видели, вспомогательный код может быть почти идентичным. Предлагаю пользователям хабра поделиться друг с другом своими вспомогательными функциями.


Вот например некоторые мои вспомогательные классы (код на C#). Все они когда-то были найдены в интернете, скопированы у коллег или написаны мной (я уже не помню). Могут содержать в себе ошибки.


Потокобезопасный Random


В C# класс Random не является потокобезопасным, но чтобы при генерации случайного числа из разных потоков в небольшом отрезке времени не получить одинаковое значение, переменную типа Random необходимо хранить статически. Решение этой проблемы может быть класс ThreadSafeRandom:


    public sealed class ThreadSafeRandom
    {
        private static readonly Random Global = new Random();
        [ThreadStatic] private static Random _local;

        public ThreadSafeRandom()
        {
            if (_local == null)
            {
                int seed;
                lock (Global)
                {
                    seed = Global.Next();
                }
                _local = new Random(seed);
            }
        }

        public int Next()
        {
            return _local.Next();
        }

        public int Next(int max)
        {
            return _local.Next(max);
        }

        public int Next(int min, int max)
        {
            return _local.Next(min, max);
        }
    }

Каждый поток будет иметь свой экземпляр Random, который будет создан при помощи случайного числа из общего для всех потоков объекта Random.


UnixTime


В C# есть класс DateTime, но к великом моему сожалению, он не содержит в себе функции для работы с UnixTime. Поэтому я написал свой статический класс UnixTimeUtil:


    public static class UnixTimeUtil
    {
        public static double UnixTimeNow => GetUnixTyime(DateTime.Now);

        public static DateTime GetDateTime(double timestamp)
        {
            var origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return origin.AddSeconds(timestamp);
        }

        public static double GetUnixTyime(DateTime date)
        {
            var origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            var diff = date - origin;
            return diff.TotalSeconds;
        }
    }

Класс позволяет конвертировать DateTime в unix time и обратно.


Randomizer


В C# чтобы получить случайное число, нужно создать объект Random, затем вызвать его метод Next. Жизнь слишком коротка, чтобы писать так много кода для таких простых операций. +Часто возникает необходимость получить не только случайное число, но и случайную строку. Чтобы не писать подобный код я использую статических потокобезопасный класс Randomizer:


 public static class Randomizer
    {
        private const string Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

        public static int GetRandomNumber()
        {
            var threadSafeRandom = new ThreadSafeRandom();
            return threadSafeRandom.Next();
        }

        public static int GetRandomNumber(int max)
        {
            var threadSafeRandom = new ThreadSafeRandom();
            return threadSafeRandom.Next(max);
        }

        public static int GetRandomNumber(int min, int max)
        {
            var threadSafeRandom = new ThreadSafeRandom();
            return threadSafeRandom.Next(min, max);
        }

        public static string GetRandomString(int length)
        {
            var stringBuilder = new StringBuilder();

            for (var i = 0; i < length; i++)
            {
                stringBuilder.Append(Chars[GetRandomNumber(0, Chars.Length - 1)]);
            }

            return stringBuilder.ToString();
        }
    }

DataAnnotationsValidator


Бывает что нужно проверить валиден ли объект модели в сервисе. При этом сама логика валидации уже находится в атрибутах модели. Для таких случает у меня есть класс DataAnnotationsValidator:


    public static class DataAnnotationsValidator
    {
        public static List<string> Validation(object entity)
        {
            var errors = new List<string>();
            var results = new List<ValidationResult>();
            var context = new ValidationContext(entity);

            if (Validator.TryValidateObject(entity, context, results, true)) 
                return null;

            errors.AddRange(results.Select(error => error.ErrorMessage));
            return errors;
        }
    }

В случаи отсутствия ошибок валидации функция вернет null, иначе список строк ошибок.


Md5Hasher и Pbkdf2Hasher


Функций получения хеша строки используются мной в каждом большом проекте. К сожалению в C# нет классов, которые имели бы простой функционал получения и сравнения хешей без лишних телодвижений. Пришлось их добавить (используется System.Security.Cryptography).


Md5Hasher:


public static class Md5Hasher
    {
        public static string Hash(string input)
        {
            using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
            {
                var inputBytes = Encoding.ASCII.GetBytes(input);
                var hashBytes = md5.ComputeHash(inputBytes);

                var sb = new StringBuilder();
                foreach (var t in hashBytes)
                {
                    sb.Append(t.ToString("X2"));
                }
                return sb.ToString();
            }
        }

        public static bool VerifyHashe(string hash, string input)
        {
            return hash == Hash(input);
        }
    }

Pbkdf2Hasher:


    public static class Pbkdf2Hasher
    {
        private const int SaltByteSize = 24;
        private const int HashByteSize = 24;
        private const int HasingIterationsCount = 10101;

        public static string Hash(string input)
        {
            byte[] salt;
            byte[] buffer2;
            if (input == null)
            {
                throw new ArgumentNullException("password");
            }
            using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(input, SaltByteSize, HasingIterationsCount))
            {
                salt = bytes.Salt;
                buffer2 = bytes.GetBytes(HashByteSize);
            }
            byte[] dst = new byte[(SaltByteSize + HashByteSize) + 1];
            Buffer.BlockCopy(salt, 0, dst, 1, SaltByteSize);
            Buffer.BlockCopy(buffer2, 0, dst, SaltByteSize + 1, HashByteSize);
            return Convert.ToBase64String(dst);
        }

        public static bool VerifyHashe(string hash, string input)
        {
            byte[] _passwordHashBytes;

            int _arrayLen = (SaltByteSize + HashByteSize) + 1;

            if (hash == null)
            {
                return false;
            }

            if (input == null)
            {
                throw new ArgumentNullException("password");
            }

            byte[] src = Convert.FromBase64String(hash);

            if ((src.Length != _arrayLen) || (src[0] != 0))
            {
                return false;
            }

            byte[] _currentSaltBytes = new byte[SaltByteSize];
            Buffer.BlockCopy(src, 1, _currentSaltBytes, 0, SaltByteSize);

            byte[] _currentHashBytes = new byte[HashByteSize];
            Buffer.BlockCopy(src, SaltByteSize + 1, _currentHashBytes, 0, HashByteSize);

            using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(input, _currentSaltBytes, HasingIterationsCount))
            {
                _passwordHashBytes = bytes.GetBytes(SaltByteSize);
            }

            return AreHashesEqual(_currentHashBytes, _passwordHashBytes);
        }

        private static bool AreHashesEqual(byte[] firstHash, byte[] secondHash)
        {
            int _minHashLength = firstHash.Length <= secondHash.Length ? firstHash.Length : secondHash.Length;
            var xor = firstHash.Length ^ secondHash.Length;
            for (int i = 0; i < _minHashLength; i++)
                xor |= firstHash[i] ^ secondHash[i];
            return 0 == xor;
        }
    }

А какие вспомогательные классы используете вы?

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.