Refactoring
This commit is contained in:
84
AMWD.Common/Extensions/CryptographyHelperExtensions.cs
Normal file
84
AMWD.Common/Extensions/CryptographyHelperExtensions.cs
Normal 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
|
||||
}
|
||||
}
|
||||
182
AMWD.Common/Extensions/DateTimeExtensions.cs
Normal file
182
AMWD.Common/Extensions/DateTimeExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
AMWD.Common/Extensions/EnumExtensions.cs
Normal file
44
AMWD.Common/Extensions/EnumExtensions.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
41
AMWD.Common/Extensions/ExceptionExtensions.cs
Normal file
41
AMWD.Common/Extensions/ExceptionExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
158
AMWD.Common/Extensions/JsonExtensions.cs
Normal file
158
AMWD.Common/Extensions/JsonExtensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
84
AMWD.Common/Extensions/ReaderWriterLockSlimExtensions.cs
Normal file
84
AMWD.Common/Extensions/ReaderWriterLockSlimExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
121
AMWD.Common/Extensions/StringExtensions.cs
Normal file
121
AMWD.Common/Extensions/StringExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user