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