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

в 18:08, , рубрики: .net, C#, Программирование

Введение

Наверное в каждого большом проекте есть папки с названиями 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;
        }
    }

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

Автор: рыцарь со стволом

Источник

Поделиться

* - обязательные к заполнению поля