1
0

Refactoring

This commit is contained in:
2021-10-22 21:05:37 +02:00
commit ca9de13c9e
43 changed files with 5145 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
namespace System.Security.Cryptography
{
/// <summary>
/// Provides extension methods for the <see cref="CryptographyHelper"/> class.
/// </summary>
public static class CryptographyHelperExtensions
{
#region Hashing
#region MD5
/// <summary>
/// Computes a hash value from a string using the MD5 algorithm.
/// </summary>
/// <param name="str">The string to hash, using UTF-8 encoding.</param>
/// <returns>The MD5 hash value, in hexadecimal notation.</returns>
public static string Md5(this string str) => CryptographyHelper.Md5(str);
/// <summary>
/// Computes a hash from a byte array value using the MD5 algorithm.
/// </summary>
/// <param name="bytes">The byte array.</param>
/// <returns>The MD5 hash value, in hexadecimal notation.</returns>
public static string Md5(this byte[] bytes) => CryptographyHelper.Md5(bytes);
#endregion MD5
#region SHA-1
/// <summary>
/// Computes a hash value from a string using the SHA-1 algorithm.
/// </summary>
/// <param name="str">The string to hash, using UTF-8 encoding.</param>
/// <returns>The SHA-1 hash value, in hexadecimal notation.</returns>
public static string Sha1(this string str) => CryptographyHelper.Sha1(str);
/// <summary>
/// Computes a hash from a byte array value using the SHA-1 algorithm.
/// </summary>
/// <param name="bytes">The byte array.</param>
/// <returns>The SHA-1 hash value, in hexadecimal notation.</returns>
public static string Sha1(this byte[] bytes) => CryptographyHelper.Sha1(bytes);
#endregion SHA-1
#region SHA-256
/// <summary>
/// Computes a hash value from a string using the SHA-256 algorithm.
/// </summary>
/// <param name="str">The string to hash, using UTF-8 encoding.</param>
/// <returns>The SHA-256 hash value, in hexadecimal notation.</returns>
public static string Sha256(string str) => CryptographyHelper.Sha256(str);
/// <summary>
/// Computes a hash from a byte array value using the SHA-256 algorithm.
/// </summary>
/// <param name="bytes">The byte array.</param>
/// <returns>The SHA-256 hash value, in hexadecimal notation.</returns>
public static string Sha256(byte[] bytes) => CryptographyHelper.Sha256(bytes);
#endregion SHA-256
#region SHA-512
/// <summary>
/// Computes a hash value from a string using the SHA-512 algorithm.
/// </summary>
/// <param name="str">The string to hash, using UTF-8 encoding.</param>
/// <returns>The SHA-512 hash value, in hexadecimal notation.</returns>
public static string Sha512(this string str) => CryptographyHelper.Sha512(str);
/// <summary>
/// Computes a hash from a byte array value using the SHA-512 algorithm.
/// </summary>
/// <param name="bytes">The byte array.</param>
/// <returns>The SHA-512 hash value, in hexadecimal notation.</returns>
public static string Sha512(this byte[] bytes) => CryptographyHelper.Sha512(bytes);
#endregion SHA-512
#endregion Hashing
}
}

View File

@@ -0,0 +1,182 @@
using System.Text;
namespace System
{
/// <summary>
/// Provides extension methods for date and time manipulation.
/// </summary>
public static class DateTimeExtensions
{
#region Kind
/// <summary>
/// Specifies the <see cref="DateTime.Kind"/> as UTC.
/// </summary>
/// <param name="dt">The <see cref="DateTime"/> instance.</param>
/// <returns>A <see cref="DateTime"/> with correct <see cref="DateTime.Kind"/>.</returns>
public static DateTime AsUtc(this DateTime dt)
{
return dt.Kind switch
{
DateTimeKind.Local => dt.ToUniversalTime(),
DateTimeKind.Utc => dt,
_ => DateTime.SpecifyKind(dt, DateTimeKind.Utc),
};
}
/// <summary>
/// Specifies the <see cref="DateTime.Kind"/> as local time.
/// </summary>
/// <param name="dt">The <see cref="DateTime"/> instance.</param>
/// <returns>A <see cref="DateTime"/> with correct <see cref="DateTime.Kind"/>.</returns>
public static DateTime AsLocal(this DateTime dt)
{
return dt.Kind switch
{
DateTimeKind.Local => dt,
DateTimeKind.Utc => dt.ToLocalTime(),
_ => DateTime.SpecifyKind(dt, DateTimeKind.Local),
};
}
#endregion Kind
/// <summary>
/// Aligns the <see cref="TimeSpan"/> to the clock.
/// </summary>
/// <param name="timeSpan">The timespan to align.</param>
/// <param name="offset">A specific offset to the timespan.</param>
/// <returns>The timespan until the aligned time.</returns>
public static TimeSpan GetAlignedInterval(this TimeSpan timeSpan, TimeSpan offset = default)
{
var now = DateTime.UtcNow;
var nextTime = new DateTime(now.Ticks / timeSpan.Ticks * timeSpan.Ticks) + offset;
if (nextTime <= now)
nextTime += timeSpan;
return nextTime - now;
}
/// <summary>
/// Prints the timespan as shortended string.
/// </summary>
/// <param name="timeSpan">The timespan</param>
/// <param name="withMilliseconds">A value indicating whether to show milliseconds.</param>
/// <returns>The timespan as string.</returns>
public static string ToShortString(this TimeSpan timeSpan, bool withMilliseconds = false)
{
var sb = new StringBuilder();
if (timeSpan.TotalDays >= 1)
sb.Append(timeSpan.Days).Append("d ");
if (timeSpan.TotalHours >= 1)
sb.Append(timeSpan.Hours).Append("h ");
if (timeSpan.TotalMinutes >= 1)
sb.Append(timeSpan.Minutes).Append("m ");
sb.Append(timeSpan.Seconds).Append("s ");
if (withMilliseconds)
sb.Append(timeSpan.Milliseconds).Append("ms");
return sb.ToString().Trim();
}
#region Round DateTime
/// <summary>
/// Rounds the <see cref="DateTime"/> to full seconds.
/// </summary>
/// <param name="dt">The time value to round.</param>
/// <returns></returns>
public static DateTime RoundToSecond(this DateTime dt)
{
return new DateTime(RoundTicks(dt.Ticks, TimeSpan.TicksPerSecond), dt.Kind);
}
/// <summary>
/// Rounds the <see cref="DateTime"/> to full minutes.
/// </summary>
/// <param name="dt">The time value to round.</param>
/// <returns></returns>
public static DateTime RoundToMinute(this DateTime dt)
{
return new DateTime(RoundTicks(dt.Ticks, TimeSpan.TicksPerMinute), dt.Kind);
}
/// <summary>
/// Rounds the <see cref="DateTime"/> to full hours.
/// </summary>
/// <param name="dt">The time value to round.</param>
/// <returns></returns>
public static DateTime RoundToHour(this DateTime dt)
{
return new DateTime(RoundTicks(dt.Ticks, TimeSpan.TicksPerHour), dt.Kind);
}
/// <summary>
/// Rounds the <see cref="DateTime"/> to full days.
/// </summary>
/// <param name="dt">The time value to round.</param>
/// <returns></returns>
public static DateTime RoundToDay(this DateTime dt)
{
return new DateTime(RoundTicks(dt.Ticks, TimeSpan.TicksPerDay), dt.Kind);
}
#endregion Round DateTime
#region Round TimeSpan
/// <summary>
/// Rounds the <see cref="TimeSpan"/> to full seconds.
/// </summary>
/// <param name="timeSpan">The time value to round.</param>
/// <returns></returns>
public static TimeSpan RoundToSecond(this TimeSpan timeSpan)
{
return new TimeSpan(RoundTicks(timeSpan.Ticks, TimeSpan.TicksPerSecond));
}
/// <summary>
/// Rounds the <see cref="TimeSpan"/> to full minutes.
/// </summary>
/// <param name="timeSpan">The time value to round.</param>
/// <returns></returns>
public static TimeSpan RoundToMinute(this TimeSpan timeSpan)
{
return new TimeSpan(RoundTicks(timeSpan.Ticks, TimeSpan.TicksPerMinute));
}
/// <summary>
/// Rounds the <see cref="TimeSpan"/> to full hours.
/// </summary>
/// <param name="timeSpan">The time value to round.</param>
/// <returns></returns>
public static TimeSpan RoundToHour(this TimeSpan timeSpan)
{
return new TimeSpan(RoundTicks(timeSpan.Ticks, TimeSpan.TicksPerHour));
}
/// <summary>
/// Rounds the <see cref="TimeSpan"/> to full days.
/// </summary>
/// <param name="timeSpan">The time value to round.</param>
/// <returns></returns>
public static TimeSpan RoundToDay(this TimeSpan timeSpan)
{
return new TimeSpan(RoundTicks(timeSpan.Ticks, TimeSpan.TicksPerDay));
}
#endregion Round TimeSpan
private static long RoundTicks(long ticks, long value)
{
return (ticks + value / 2) / value * value;
}
}
}

View File

@@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace System
{
/// <summary>
/// Extend the enum values by attribute driven methods.
/// </summary>
public static class EnumExtensions
{
/// <summary>
/// Returns a list of specific attribute type from a enum-value.
/// </summary>
/// <typeparam name="TAttribute">The attribute type.</typeparam>
/// <param name="value">The enum value.</param>
/// <returns>The attributes or null.</returns>
public static IEnumerable<TAttribute> GetAttributes<TAttribute>(this Enum value)
{
var fieldInfo = value.GetType().GetField(value.ToString());
if (fieldInfo == null)
return Array.Empty<TAttribute>();
return fieldInfo.GetCustomAttributes(typeof(TAttribute), inherit: false).Cast<TAttribute>();
}
/// <summary>
/// Returns a specific attribute from a enum-value.
/// </summary>
/// <typeparam name="TAttribute">The attribute type.</typeparam>
/// <param name="value">The enum value.</param>
/// <returns>The attribute or null.</returns>
public static TAttribute GetAttribute<TAttribute>(this Enum value)
=> value.GetAttributes<TAttribute>().FirstOrDefault();
/// <summary>
/// Returns the description from <see cref="DescriptionAttribute"/>.
/// </summary>
/// <param name="value">The enum value.</param>
/// <returns>The description or the string representation of the value.</returns>
public static string GetDescription(this Enum value)
=> value.GetAttribute<DescriptionAttribute>()?.Description ?? value.ToString();
}
}

View File

@@ -0,0 +1,41 @@
using System.Linq;
namespace System
{
/// <summary>
/// Provides extension methods for exceptions.
/// </summary>
public static class ExceptionExtensions
{
/// <summary>
/// Returns the message of the inner exception if exists otherwise the message of the exception itself.
/// </summary>
/// <param name="exception">The exception.</param>
/// <returns>The message of the inner exception or the exception itself.</returns>
public static string GetMessage(this Exception exception)
=> exception.InnerException?.Message ?? exception.Message;
/// <summary>
/// Returns the message of the exception and its inner exceptions.
/// </summary>
/// <param name="exception">The exception.</param>
/// <returns>The message of the <paramref name="exception"/> and all its inner exceptions.</returns>
public static string GetRecursiveMessage(this Exception exception)
{
if (exception is AggregateException aggregateEx)
{
return aggregateEx.InnerExceptions
.Take(3)
.Select(ex => ex.GetRecursiveMessage())
.Aggregate((a, b) => a + " " + b);
}
if (exception.InnerException != null)
{
string message = exception.Message;
message = message.ReplaceEnd(" See the inner exception for details.", "");
return message + " " + exception.InnerException.GetRecursiveMessage();
}
return exception.Message;
}
}
}

View File

@@ -0,0 +1,158 @@
using System;
using System.Collections;
using System.IO;
using System.Text;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Unclassified.Util;
namespace Newtonsoft.Json
{
/// <summary>
/// Provides extension methods to serialize and deserialize JSON values to/from objects using
/// common naming conventions.
/// </summary>
public static class JsonExtensions
{
/// <summary>
/// Common JSON serializer settings.
/// </summary>
private static readonly JsonSerializerSettings jsonSerializerSettings = new()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
/// <summary>
/// Populates an instance with values deserialized from a JSON string.
/// </summary>
/// <typeparam name="T">The type of the instance to populate.</typeparam>
/// <param name="target">The instance to populate.</param>
/// <param name="json">The JSON string to read the values from.</param>
public static void DeserializeJson<T>(this T target, string json)
{
if (!string.IsNullOrWhiteSpace(json))
JsonConvert.PopulateObject(json, target, jsonSerializerSettings);
}
/// <summary>
/// Serializes an instance to a JSON string.
/// </summary>
/// <typeparam name="T">The type of the instance to serialize.</typeparam>
/// <param name="source">The instance to serialize.</param>
/// <param name="indented">Indicates whether the JSON string is indented to make it better readable.</param>
/// <param name="useSingleQuotes">Indicates whether the JSON string uses single quotes instead of double quotes.</param>
/// <param name="useCamelCase">Indicates whether the camelCase conversion should be used.</param>
/// <param name="includeType">Indicates whether to include the instance type of <paramref name="source"/> if it is not <typeparamref name="T"/>.</param>
/// <returns>The JSON-serialized string.</returns>
public static string SerializeJson<T>(this T source, bool indented = false, bool useSingleQuotes = false, bool useCamelCase = true, bool includeType = false)
{
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
using (var jw = new JsonTextWriter(sw))
{
if (useSingleQuotes)
jw.QuoteChar = '\'';
jw.Formatting = indented ? Formatting.Indented : Formatting.None;
var serializer = useCamelCase ? JsonSerializer.Create(jsonSerializerSettings) : JsonSerializer.CreateDefault();
serializer.Error += (s, a) =>
{
a.ErrorContext.Handled = true;
};
if (includeType)
serializer.TypeNameHandling = TypeNameHandling.Auto;
serializer.Serialize(jw, source, typeof(T));
}
return sb.ToString().Trim();
}
/// <summary>
/// Deserializes a JSON string into a new instance.
/// </summary>
/// <typeparam name="T">The type of the instance to deserialize.</typeparam>
/// <param name="json">The JSON string to read the values from.</param>
/// <returns>A new instance of <typeparamref name="T"/> with the deserialized values.</returns>
public static T DeserializeJson<T>(this string json)
{
if (!string.IsNullOrWhiteSpace(json))
return JsonConvert.DeserializeObject<T>(json, jsonSerializerSettings);
return default;
}
/// <summary>
/// Converts an object into a JObject using the custom serializer settings.
/// </summary>
/// <param name="obj">The object to convert.</param>
/// <returns>A JObject representing the <paramref name="obj"/>.</returns>
public static JObject ConvertToJObject(this object obj)
{
if (obj == null)
return null;
var serializer = JsonSerializer.Create(jsonSerializerSettings);
return JObject.FromObject(obj, serializer);
}
/// <summary>
/// Converts an enumerable into a JArray using the custom serializer settings.
/// </summary>
/// <param name="array">The enumerable to convert.</param>
/// <returns>A JArray representing the <paramref name="array"/>.</returns>
public static JArray ConvertToJArray(this IEnumerable array)
{
if (array == null)
return null;
var serializer = JsonSerializer.Create(jsonSerializerSettings);
return JArray.FromObject(array, serializer);
}
/// <summary>
/// Gets a value from the object using multiple levels.
/// </summary>
/// <typeparam name="T">The type to convert the data to.</typeparam>
/// <param name="jObj">The object.</param>
/// <param name="key">The key to the value.</param>
/// <param name="defaultValue">The default value when the key was not found.</param>
/// <param name="keySplit">The character to split the key in levels (default: colon).</param>
/// <returns>The converted value.</returns>
public static T GetValue<T>(this JObject jObj, string key, T defaultValue, char keySplit = ':')
{
if (jObj == null)
return defaultValue;
string[] levels = key.Split(keySplit);
JToken lvlObj = jObj;
foreach (string level in levels)
{
if (lvlObj == null)
return defaultValue;
lvlObj = lvlObj[level];
}
if (lvlObj == null)
return defaultValue;
if (typeof(T) == typeof(string))
return (T)Convert.ChangeType(lvlObj, typeof(T));
return DeepConvert.ChangeType<T>(lvlObj);
}
/// <summary>
/// Gets a value from the object using multiple levels.
/// </summary>
/// <typeparam name="T">The type to convert the data to.</typeparam>
/// <param name="jObj">The object.</param>
/// <param name="key">The key to the value.</param>
/// <param name="keySplit">The character to split the key in levels (default: colon).</param>
/// <returns>The converted value.</returns>
public static T GetValue<T>(this JObject jObj, string key, char keySplit = ':')
=> jObj.GetValue(key, default(T), keySplit);
}
}

View File

@@ -0,0 +1,84 @@
namespace System.Threading
{
/// <summary>
/// Provides extension methods for the <see cref="ReaderWriterLockSlim"/>.
/// </summary>
public static class ReaderWriterLockSlimExtensions
{
/// <summary>
/// Acquires a read lock on a lock object that can be released with an
/// <see cref="IDisposable"/> instance.
/// </summary>
/// <param name="rwLock">The lock object.</param>
/// <param name="timeoutMilliseconds">The number of milliseconds to wait, or -1
/// (<see cref="Timeout.Infinite"/>) to wait indefinitely.</param>
/// <returns>An <see cref="IDisposable"/> instance to release the lock.</returns>
public static IDisposable GetReadLock(this ReaderWriterLockSlim rwLock, int timeoutMilliseconds = -1)
{
if (!rwLock.TryEnterReadLock(timeoutMilliseconds))
throw new TimeoutException("The read lock could not be acquired.");
return new RWLockDisposable(rwLock, 1);
}
/// <summary>
/// Acquires a upgradeable read lock on a lock object that can be released with an
/// <see cref="IDisposable"/> instance. The lock can be upgraded to a write lock temporarily
/// with <see cref="GetWriteLock"/> or until the lock is released with
/// <see cref="ReaderWriterLockSlim.EnterWriteLock"/> alone.
/// </summary>
/// <param name="rwLock">The lock object.</param>
/// <param name="timeoutMilliseconds">The number of milliseconds to wait, or -1
/// (<see cref="Timeout.Infinite"/>) to wait indefinitely.</param>
/// <returns>An <see cref="IDisposable"/> instance to release the lock. If the lock was
/// upgraded to a write lock, that will be released as well.</returns>
public static IDisposable GetUpgradeableReadLock(this ReaderWriterLockSlim rwLock, int timeoutMilliseconds = -1)
{
if (!rwLock.TryEnterUpgradeableReadLock(timeoutMilliseconds))
throw new TimeoutException("The upgradeable read lock could not be acquired.");
return new RWLockDisposable(rwLock, 2);
}
/// <summary>
/// Acquires a write lock on a lock object that can be released with an
/// <see cref="IDisposable"/> instance.
/// </summary>
/// <param name="rwLock">The lock object.</param>
/// <param name="timeoutMilliseconds">The number of milliseconds to wait, or -1
/// (<see cref="Timeout.Infinite"/>) to wait indefinitely.</param>
/// <returns>An <see cref="IDisposable"/> instance to release the lock.</returns>
public static IDisposable GetWriteLock(this ReaderWriterLockSlim rwLock, int timeoutMilliseconds = -1)
{
if (!rwLock.TryEnterWriteLock(timeoutMilliseconds))
throw new TimeoutException("The write lock could not be acquired.");
return new RWLockDisposable(rwLock, 3);
}
private struct RWLockDisposable : IDisposable
{
private readonly ReaderWriterLockSlim rwLock;
private int lockMode;
public RWLockDisposable(ReaderWriterLockSlim rwLock, int lockMode)
{
this.rwLock = rwLock;
this.lockMode = lockMode;
}
public void Dispose()
{
if (lockMode == 1)
rwLock.ExitReadLock();
if (lockMode == 2 && rwLock.IsWriteLockHeld) // Upgraded with EnterWriteLock alone
rwLock.ExitWriteLock();
if (lockMode == 2)
rwLock.ExitUpgradeableReadLock();
if (lockMode == 3)
rwLock.ExitWriteLock();
lockMode = 0;
}
}
}
}

View File

@@ -0,0 +1,121 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace System
{
/// <summary>
/// String extensions.
/// </summary>
public static class StringExtensions
{
/// <summary>
/// Converts a hex string into a byte array.
/// </summary>
/// <param name="hexString">The hex encoded string.</param>
/// <param name="delimiter">A delimiter between the bytes (e.g. for MAC address).</param>
/// <returns>The bytes.</returns>
public static IEnumerable<byte> HexToBytes(this string hexString, string delimiter = "")
{
if (string.IsNullOrWhiteSpace(hexString))
yield break;
string str = string.IsNullOrEmpty(delimiter) ? hexString : hexString.Replace(delimiter, "");
if (str.Length % 2 == 1)
yield break;
for (int i = 0; i < str.Length; i += 2)
yield return Convert.ToByte(str.Substring(i, 2), 16);
}
/// <summary>
/// Converts a byte collection into a hex string.
/// </summary>
/// <param name="bytes">The bytes.</param>
/// <param name="delimiter">A delimiter to set between the bytes (e.g. for MAC address).</param>
/// <returns>The hex encoded string.</returns>
public static string BytesToHex(this IEnumerable<byte> bytes, string delimiter = "")
{
if (bytes?.Any() != true)
return null;
return string.Join(delimiter, bytes.Select(b => b.ToString("x2")));
}
/// <summary>
/// Encodes a string to the hexadecimal system (base 16).
/// </summary>
/// <param name="str"></param>
/// <param name="encoding"></param>
/// <returns></returns>
public static string HexEncode(this string str, Encoding encoding = null)
{
if (string.IsNullOrEmpty(str))
return str;
return (encoding ?? Encoding.Default).GetBytes(str).BytesToHex();
}
/// <summary>
/// Decodes a string from the hexadecimal system (base 16).
/// </summary>
/// <param name="str"></param>
/// <param name="encoding"></param>
/// <returns></returns>
public static string HexDecode(this string str, Encoding encoding = null)
{
if (string.IsNullOrEmpty(str))
return str;
return (encoding ?? Encoding.Default).GetString(str.HexToBytes().ToArray());
}
/// <summary>
/// Encodes a string to base64.
/// </summary>
/// <param name="str"></param>
/// <param name="encoding"></param>
/// <returns></returns>
public static string Base64Encode(this string str, Encoding encoding = null)
=> Convert.ToBase64String((encoding ?? Encoding.Default).GetBytes(str));
/// <summary>
/// Decodes a string from base64.
/// </summary>
/// <param name="str"></param>
/// <param name="encoding"></param>
/// <returns></returns>
public static string Base64Decode(this string str, Encoding encoding = null)
=> (encoding ?? Encoding.Default).GetString(Convert.FromBase64String(str));
/// <summary>
/// Replaces the search substring with the replacement when it was found at the beginning of the string.
/// </summary>
/// <param name="str">The string.</param>
/// <param name="search">The searched substring.</param>
/// <param name="replacement">The replacement.</param>
/// <returns></returns>
public static string ReplaceStart(this string str, string search, string replacement)
{
if (str.StartsWith(search))
return replacement + str.Substring(search.Length);
return str;
}
/// <summary>
/// Replaces the search substring with the replacement when it was found at the end of the string.
/// </summary>
/// <param name="str">The string.</param>
/// <param name="search">The searched substring.</param>
/// <param name="replacement">The replacement.</param>
/// <returns></returns>
public static string ReplaceEnd(this string str, string search, string replacement)
{
if (str.EndsWith(search))
return str.Substring(0, str.Length - search.Length) + replacement;
return str;
}
}
}