mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
Code style - Use file scoped namespaces
Code style - Ran code cleanup
This commit is contained in:
1 parent
e68e16df4d
commit
f6090dc296
691 files changed
+37301
-38119
No files matched your search
@@ -1,32 +1,31 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Artemis.Core
|
||||
namespace Artemis.Core;
|
||||
|
||||
internal static class DirectoryInfoExtensions
|
||||
{
|
||||
internal static class DirectoryInfoExtensions
|
||||
public static void CopyFilesRecursively(this DirectoryInfo source, DirectoryInfo target)
|
||||
{
|
||||
public static void CopyFilesRecursively(this DirectoryInfo source, DirectoryInfo target)
|
||||
foreach (DirectoryInfo dir in source.GetDirectories())
|
||||
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
|
||||
foreach (FileInfo file in source.GetFiles())
|
||||
file.CopyTo(Path.Combine(target.FullName, file.Name));
|
||||
}
|
||||
|
||||
public static void DeleteRecursively(this DirectoryInfo baseDir)
|
||||
{
|
||||
if (!baseDir.Exists)
|
||||
return;
|
||||
|
||||
foreach (DirectoryInfo dir in baseDir.EnumerateDirectories())
|
||||
DeleteRecursively(dir);
|
||||
FileInfo[] files = baseDir.GetFiles();
|
||||
foreach (FileInfo file in files)
|
||||
{
|
||||
foreach (DirectoryInfo dir in source.GetDirectories())
|
||||
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
|
||||
foreach (FileInfo file in source.GetFiles())
|
||||
file.CopyTo(Path.Combine(target.FullName, file.Name));
|
||||
file.IsReadOnly = false;
|
||||
file.Delete();
|
||||
}
|
||||
|
||||
public static void DeleteRecursively(this DirectoryInfo baseDir)
|
||||
{
|
||||
if (!baseDir.Exists)
|
||||
return;
|
||||
|
||||
foreach (DirectoryInfo dir in baseDir.EnumerateDirectories())
|
||||
DeleteRecursively(dir);
|
||||
FileInfo[] files = baseDir.GetFiles();
|
||||
foreach (FileInfo file in files)
|
||||
{
|
||||
file.IsReadOnly = false;
|
||||
file.Delete();
|
||||
}
|
||||
|
||||
baseDir.Delete();
|
||||
}
|
||||
baseDir.Delete();
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,21 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Artemis.Core
|
||||
namespace Artemis.Core;
|
||||
|
||||
/// <summary>
|
||||
/// A static class providing <see cref="double" /> extensions
|
||||
/// </summary>
|
||||
public static class DoubleExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// A static class providing <see cref="double" /> extensions
|
||||
/// Rounds the provided number away to zero and casts the result to an <see cref="int" />
|
||||
/// </summary>
|
||||
public static class DoubleExtensions
|
||||
/// <param name="number">The number to round</param>
|
||||
/// <returns>The rounded number as an integer</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int RoundToInt(this double number)
|
||||
{
|
||||
/// <summary>
|
||||
/// Rounds the provided number away to zero and casts the result to an <see cref="int" />
|
||||
/// </summary>
|
||||
/// <param name="number">The number to round</param>
|
||||
/// <returns>The rounded number as an integer</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int RoundToInt(this double number)
|
||||
{
|
||||
return (int) Math.Round(number, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
return (int) Math.Round(number, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,21 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Artemis.Core
|
||||
namespace Artemis.Core;
|
||||
|
||||
/// <summary>
|
||||
/// A static class providing <see cref="float" /> extensions
|
||||
/// </summary>
|
||||
public static class FloatExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// A static class providing <see cref="float" /> extensions
|
||||
/// Rounds the provided number away to zero and casts the result to an <see cref="int" />
|
||||
/// </summary>
|
||||
public static class FloatExtensions
|
||||
/// <param name="number">The number to round</param>
|
||||
/// <returns>The rounded number as an integer</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int RoundToInt(this float number)
|
||||
{
|
||||
/// <summary>
|
||||
/// Rounds the provided number away to zero and casts the result to an <see cref="int" />
|
||||
/// </summary>
|
||||
/// <param name="number">The number to round</param>
|
||||
/// <returns>The rounded number as an integer</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int RoundToInt(this float number)
|
||||
{
|
||||
return (int) MathF.Round(number, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
return (int) MathF.Round(number, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
}
|
||||
@@ -19,35 +19,33 @@
|
||||
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Artemis.Core
|
||||
namespace Artemis.Core;
|
||||
|
||||
/// <summary>
|
||||
/// A static class providing <see cref="IEnumerable{T}" /> extensions
|
||||
/// </summary>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static class IEnumerableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// A static class providing <see cref="IEnumerable{T}" /> extensions
|
||||
/// Returns the index of the provided element inside the read only collection
|
||||
/// </summary>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static class IEnumerableExtensions
|
||||
/// <typeparam name="T">The type of element to find</typeparam>
|
||||
/// <param name="self">The collection to search in</param>
|
||||
/// <param name="elementToFind">The element to find</param>
|
||||
/// <returns>If found, the index of the element to find; otherwise -1</returns>
|
||||
public static int IndexOf<T>(this IReadOnlyCollection<T> self, T elementToFind)
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the index of the provided element inside the read only collection
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of element to find</typeparam>
|
||||
/// <param name="self">The collection to search in</param>
|
||||
/// <param name="elementToFind">The element to find</param>
|
||||
/// <returns>If found, the index of the element to find; otherwise -1</returns>
|
||||
public static int IndexOf<T>(this IReadOnlyCollection<T> self, T elementToFind)
|
||||
int i = 0;
|
||||
foreach (T element in self)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (T element in self)
|
||||
{
|
||||
if (Equals(element, elementToFind))
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
if (Equals(element, elementToFind))
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,41 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Artemis.Core
|
||||
namespace Artemis.Core;
|
||||
|
||||
/// <summary>
|
||||
/// A static class providing <see cref="Process" /> extensions
|
||||
/// </summary>
|
||||
[SuppressMessage("Design", "CA1060:Move pinvokes to native methods class", Justification = "I don't care, piss off")]
|
||||
public static class ProcessExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// A static class providing <see cref="Process" /> extensions
|
||||
/// Gets the file name of the given process
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1060:Move pinvokes to native methods class", Justification = "I don't care, piss off")]
|
||||
public static class ProcessExtensions
|
||||
/// <param name="p">The process</param>
|
||||
/// <returns>The filename of the given process</returns>
|
||||
public static string GetProcessFilename(this Process p)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the file name of the given process
|
||||
/// </summary>
|
||||
/// <param name="p">The process</param>
|
||||
/// <returns>The filename of the given process</returns>
|
||||
public static string GetProcessFilename(this Process p)
|
||||
{
|
||||
int capacity = 2000;
|
||||
StringBuilder builder = new(capacity);
|
||||
IntPtr ptr = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, p.Id);
|
||||
if (!QueryFullProcessImageName(ptr, 0, builder, ref capacity)) return string.Empty;
|
||||
int capacity = 2000;
|
||||
StringBuilder builder = new(capacity);
|
||||
IntPtr ptr = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, p.Id);
|
||||
if (!QueryFullProcessImageName(ptr, 0, builder, ref capacity)) return string.Empty;
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] int dwFlags, [Out] StringBuilder lpExeName, ref int lpdwSize);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] int dwFlags, [Out] StringBuilder lpExeName, ref int lpdwSize);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, int processId);
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, int processId);
|
||||
|
||||
[Flags]
|
||||
private enum ProcessAccessFlags : uint
|
||||
{
|
||||
QueryLimitedInformation = 0x00001000
|
||||
}
|
||||
[Flags]
|
||||
private enum ProcessAccessFlags : uint
|
||||
{
|
||||
QueryLimitedInformation = 0x00001000
|
||||
}
|
||||
}
|
||||
@@ -2,39 +2,38 @@
|
||||
using RGB.NET.Core;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core
|
||||
namespace Artemis.Core;
|
||||
|
||||
internal static class RgbDeviceExtensions
|
||||
{
|
||||
internal static class RgbDeviceExtensions
|
||||
public static string GetDeviceIdentifier(this IRGBDevice rgbDevice)
|
||||
{
|
||||
public static string GetDeviceIdentifier(this IRGBDevice rgbDevice)
|
||||
{
|
||||
StringBuilder builder = new();
|
||||
builder.Append(rgbDevice.DeviceInfo.DeviceName);
|
||||
builder.Append('-');
|
||||
builder.Append(rgbDevice.DeviceInfo.Manufacturer);
|
||||
builder.Append('-');
|
||||
builder.Append(rgbDevice.DeviceInfo.Model);
|
||||
builder.Append('-');
|
||||
builder.Append(rgbDevice.DeviceInfo.DeviceType);
|
||||
return builder.ToString();
|
||||
}
|
||||
StringBuilder builder = new();
|
||||
builder.Append(rgbDevice.DeviceInfo.DeviceName);
|
||||
builder.Append('-');
|
||||
builder.Append(rgbDevice.DeviceInfo.Manufacturer);
|
||||
builder.Append('-');
|
||||
builder.Append(rgbDevice.DeviceInfo.Model);
|
||||
builder.Append('-');
|
||||
builder.Append(rgbDevice.DeviceInfo.DeviceType);
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
internal static class RgbRectangleExtensions
|
||||
{
|
||||
public static SKRect ToSKRect(this Rectangle rectangle)
|
||||
{
|
||||
return SKRect.Create(
|
||||
rectangle.Location.X,
|
||||
rectangle.Location.Y,
|
||||
rectangle.Size.Width,
|
||||
rectangle.Size.Height
|
||||
);
|
||||
}
|
||||
|
||||
internal static class RgbRectangleExtensions
|
||||
public static SKRectI ToSKRectI(this Rectangle rectangle)
|
||||
{
|
||||
public static SKRect ToSKRect(this Rectangle rectangle)
|
||||
{
|
||||
return SKRect.Create(
|
||||
rectangle.Location.X,
|
||||
rectangle.Location.Y,
|
||||
rectangle.Size.Width,
|
||||
rectangle.Size.Height
|
||||
);
|
||||
}
|
||||
|
||||
public static SKRectI ToSKRectI(this Rectangle rectangle)
|
||||
{
|
||||
return SKRectI.Round(ToSKRect(rectangle));
|
||||
}
|
||||
return SKRectI.Round(ToSKRect(rectangle));
|
||||
}
|
||||
}
|
||||
@@ -2,77 +2,76 @@
|
||||
using RGB.NET.Core;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core
|
||||
namespace Artemis.Core;
|
||||
|
||||
/// <summary>
|
||||
/// A static class providing <see cref="SKColor" /> extensions
|
||||
/// </summary>
|
||||
public static class SKColorExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// A static class providing <see cref="SKColor" /> extensions
|
||||
/// Converts hte SKColor to an RGB.NET color
|
||||
/// </summary>
|
||||
public static class SKColorExtensions
|
||||
/// <param name="color">The color to convert</param>
|
||||
/// <returns>The RGB.NET color</returns>
|
||||
public static Color ToRgbColor(this SKColor color)
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts hte SKColor to an RGB.NET color
|
||||
/// </summary>
|
||||
/// <param name="color">The color to convert</param>
|
||||
/// <returns>The RGB.NET color</returns>
|
||||
public static Color ToRgbColor(this SKColor color)
|
||||
{
|
||||
return new Color(color.Alpha, color.Red, color.Green, color.Blue);
|
||||
}
|
||||
return new Color(color.Alpha, color.Red, color.Green, color.Blue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates a color between the <paramref name="from" /> and <paramref name="to" /> color.
|
||||
/// </summary>
|
||||
/// <param name="from">The first color</param>
|
||||
/// <param name="to">The second color</param>
|
||||
/// <param name="progress">A value between 0 and 1</param>
|
||||
/// <returns>The interpolated color</returns>
|
||||
public static SKColor Interpolate(this SKColor from, SKColor to, float progress)
|
||||
{
|
||||
int redDiff = to.Red - from.Red;
|
||||
int greenDiff = to.Green - from.Green;
|
||||
int blueDiff = to.Blue - from.Blue;
|
||||
int alphaDiff = to.Alpha - from.Alpha;
|
||||
/// <summary>
|
||||
/// Interpolates a color between the <paramref name="from" /> and <paramref name="to" /> color.
|
||||
/// </summary>
|
||||
/// <param name="from">The first color</param>
|
||||
/// <param name="to">The second color</param>
|
||||
/// <param name="progress">A value between 0 and 1</param>
|
||||
/// <returns>The interpolated color</returns>
|
||||
public static SKColor Interpolate(this SKColor from, SKColor to, float progress)
|
||||
{
|
||||
int redDiff = to.Red - from.Red;
|
||||
int greenDiff = to.Green - from.Green;
|
||||
int blueDiff = to.Blue - from.Blue;
|
||||
int alphaDiff = to.Alpha - from.Alpha;
|
||||
|
||||
return new SKColor(
|
||||
ClampToByte(from.Red + redDiff * progress),
|
||||
ClampToByte(from.Green + greenDiff * progress),
|
||||
ClampToByte(from.Blue + blueDiff * progress),
|
||||
ClampToByte(from.Alpha + alphaDiff * progress)
|
||||
);
|
||||
}
|
||||
return new SKColor(
|
||||
ClampToByte(from.Red + redDiff * progress),
|
||||
ClampToByte(from.Green + greenDiff * progress),
|
||||
ClampToByte(from.Blue + blueDiff * progress),
|
||||
ClampToByte(from.Alpha + alphaDiff * progress)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the two colors together
|
||||
/// </summary>
|
||||
/// <param name="a">The first color</param>
|
||||
/// <param name="b">The second color</param>
|
||||
/// <returns>The sum of the two colors</returns>
|
||||
public static SKColor Sum(this SKColor a, SKColor b)
|
||||
{
|
||||
return new SKColor(
|
||||
ClampToByte(a.Red + b.Red),
|
||||
ClampToByte(a.Green + b.Green),
|
||||
ClampToByte(a.Blue + b.Blue),
|
||||
ClampToByte(a.Alpha + b.Alpha)
|
||||
);
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds the two colors together
|
||||
/// </summary>
|
||||
/// <param name="a">The first color</param>
|
||||
/// <param name="b">The second color</param>
|
||||
/// <returns>The sum of the two colors</returns>
|
||||
public static SKColor Sum(this SKColor a, SKColor b)
|
||||
{
|
||||
return new SKColor(
|
||||
ClampToByte(a.Red + b.Red),
|
||||
ClampToByte(a.Green + b.Green),
|
||||
ClampToByte(a.Blue + b.Blue),
|
||||
ClampToByte(a.Alpha + b.Alpha)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Darkens the color by the specified amount
|
||||
/// </summary>
|
||||
/// <param name="c">The color to darken</param>
|
||||
/// <param name="amount">The brightness of the new color</param>
|
||||
/// <returns>The darkened color</returns>
|
||||
public static SKColor Darken(this SKColor c, float amount)
|
||||
{
|
||||
c.ToHsl(out float h, out float s, out float l);
|
||||
l *= 1f - amount;
|
||||
return SKColor.FromHsl(h, s, l);
|
||||
}
|
||||
/// <summary>
|
||||
/// Darkens the color by the specified amount
|
||||
/// </summary>
|
||||
/// <param name="c">The color to darken</param>
|
||||
/// <param name="amount">The brightness of the new color</param>
|
||||
/// <returns>The darkened color</returns>
|
||||
public static SKColor Darken(this SKColor c, float amount)
|
||||
{
|
||||
c.ToHsl(out float h, out float s, out float l);
|
||||
l *= 1f - amount;
|
||||
return SKColor.FromHsl(h, s, l);
|
||||
}
|
||||
|
||||
private static byte ClampToByte(float value)
|
||||
{
|
||||
return (byte) Math.Clamp(value, 0, 255);
|
||||
}
|
||||
private static byte ClampToByte(float value)
|
||||
{
|
||||
return (byte) Math.Clamp(value, 0, 255);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core
|
||||
namespace Artemis.Core;
|
||||
|
||||
internal static class SKPaintExtensions
|
||||
{
|
||||
internal static class SKPaintExtensions
|
||||
internal static void DisposeSelfAndProperties(this SKPaint paint)
|
||||
{
|
||||
internal static void DisposeSelfAndProperties(this SKPaint paint)
|
||||
{
|
||||
paint.ImageFilter?.Dispose();
|
||||
paint.ColorFilter?.Dispose();
|
||||
paint.MaskFilter?.Dispose();
|
||||
paint.Shader?.Dispose();
|
||||
paint.Dispose();
|
||||
}
|
||||
paint.ImageFilter?.Dispose();
|
||||
paint.ColorFilter?.Dispose();
|
||||
paint.MaskFilter?.Dispose();
|
||||
paint.Shader?.Dispose();
|
||||
paint.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -25,110 +25,108 @@
|
||||
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Artemis.Core
|
||||
namespace Artemis.Core;
|
||||
|
||||
internal static class StreamExtensions
|
||||
{
|
||||
internal static class StreamExtensions
|
||||
private const int DefaultBufferSize = 81920;
|
||||
|
||||
/// <summary>
|
||||
/// Copies a stream to another stream
|
||||
/// </summary>
|
||||
/// <param name="source">The source <see cref="Stream" /> to copy from</param>
|
||||
/// <param name="sourceLength">The length of the source stream, if known - used for progress reporting</param>
|
||||
/// <param name="destination">The destination <see cref="Stream" /> to copy to</param>
|
||||
/// <param name="bufferSize">The size of the copy block buffer</param>
|
||||
/// <param name="progress">An <see cref="IProgress{T}" /> implementation for reporting progress</param>
|
||||
/// <param name="cancellationToken">A cancellation token</param>
|
||||
/// <returns>A task representing the operation</returns>
|
||||
public static async Task CopyToAsync(
|
||||
this Stream source,
|
||||
long sourceLength,
|
||||
Stream destination,
|
||||
int bufferSize,
|
||||
IProgress<(long, long)> progress,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
private const int DefaultBufferSize = 81920;
|
||||
if (source == null)
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
if (!source.CanRead)
|
||||
throw new ArgumentException("Has to be readable", nameof(source));
|
||||
if (destination == null)
|
||||
throw new ArgumentNullException(nameof(destination));
|
||||
if (!destination.CanWrite)
|
||||
throw new ArgumentException("Has to be writable", nameof(destination));
|
||||
if (bufferSize <= 0)
|
||||
bufferSize = DefaultBufferSize;
|
||||
|
||||
/// <summary>
|
||||
/// Copies a stream to another stream
|
||||
/// </summary>
|
||||
/// <param name="source">The source <see cref="Stream" /> to copy from</param>
|
||||
/// <param name="sourceLength">The length of the source stream, if known - used for progress reporting</param>
|
||||
/// <param name="destination">The destination <see cref="Stream" /> to copy to</param>
|
||||
/// <param name="bufferSize">The size of the copy block buffer</param>
|
||||
/// <param name="progress">An <see cref="IProgress{T}" /> implementation for reporting progress</param>
|
||||
/// <param name="cancellationToken">A cancellation token</param>
|
||||
/// <returns>A task representing the operation</returns>
|
||||
public static async Task CopyToAsync(
|
||||
this Stream source,
|
||||
long sourceLength,
|
||||
Stream destination,
|
||||
int bufferSize,
|
||||
IProgress<(long, long)> progress,
|
||||
CancellationToken cancellationToken)
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
long totalBytesRead = 0;
|
||||
int bytesRead;
|
||||
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
if (!source.CanRead)
|
||||
throw new ArgumentException("Has to be readable", nameof(source));
|
||||
if (destination == null)
|
||||
throw new ArgumentNullException(nameof(destination));
|
||||
if (!destination.CanWrite)
|
||||
throw new ArgumentException("Has to be writable", nameof(destination));
|
||||
if (bufferSize <= 0)
|
||||
bufferSize = DefaultBufferSize;
|
||||
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
long totalBytesRead = 0;
|
||||
int bytesRead;
|
||||
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
|
||||
{
|
||||
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
|
||||
totalBytesRead += bytesRead;
|
||||
progress?.Report((totalBytesRead, sourceLength));
|
||||
}
|
||||
|
||||
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
|
||||
totalBytesRead += bytesRead;
|
||||
progress?.Report((totalBytesRead, sourceLength));
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies a stream to another stream
|
||||
/// </summary>
|
||||
/// <param name="source">The source <see cref="Stream" /> to copy from</param>
|
||||
/// <param name="sourceLength">The length of the source stream, if known - used for progress reporting</param>
|
||||
/// <param name="destination">The destination <see cref="Stream" /> to copy to</param>
|
||||
/// <param name="progress">An <see cref="IProgress{T}" /> implementation for reporting progress</param>
|
||||
/// <param name="cancellationToken">A cancellation token</param>
|
||||
/// <returns>A task representing the operation</returns>
|
||||
public static Task CopyToAsync(this Stream source, long sourceLength, Stream destination, IProgress<(long, long)> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
return CopyToAsync(source, sourceLength, destination, 0, progress, cancellationToken);
|
||||
}
|
||||
progress?.Report((totalBytesRead, sourceLength));
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies a stream to another stream
|
||||
/// </summary>
|
||||
/// <param name="source">The source <see cref="Stream" /> to copy from</param>
|
||||
/// <param name="destination">The destination <see cref="Stream" /> to copy to</param>
|
||||
/// <param name="progress">An <see cref="IProgress{T}" /> implementation for reporting progress</param>
|
||||
/// <param name="cancellationToken">A cancellation token</param>
|
||||
/// <returns>A task representing the operation</returns>
|
||||
public static Task CopyToAsync(this Stream source, Stream destination, IProgress<(long, long)> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
return CopyToAsync(source, 0L, destination, 0, progress, cancellationToken);
|
||||
}
|
||||
/// <summary>
|
||||
/// Copies a stream to another stream
|
||||
/// </summary>
|
||||
/// <param name="source">The source <see cref="Stream" /> to copy from</param>
|
||||
/// <param name="sourceLength">The length of the source stream, if known - used for progress reporting</param>
|
||||
/// <param name="destination">The destination <see cref="Stream" /> to copy to</param>
|
||||
/// <param name="progress">An <see cref="IProgress{T}" /> implementation for reporting progress</param>
|
||||
/// <param name="cancellationToken">A cancellation token</param>
|
||||
/// <returns>A task representing the operation</returns>
|
||||
public static Task CopyToAsync(this Stream source, long sourceLength, Stream destination, IProgress<(long, long)> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
return CopyToAsync(source, sourceLength, destination, 0, progress, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies a stream to another stream
|
||||
/// </summary>
|
||||
/// <param name="source">The source <see cref="Stream" /> to copy from</param>
|
||||
/// <param name="sourceLength">The length of the source stream, if known - used for progress reporting</param>
|
||||
/// <param name="destination">The destination <see cref="Stream" /> to copy to</param>
|
||||
/// <param name="progress">An <see cref="IProgress{T}" /> implementation for reporting progress</param>
|
||||
/// <returns>A task representing the operation</returns>
|
||||
public static Task CopyToAsync(this Stream source, long sourceLength, Stream destination, IProgress<(long, long)> progress)
|
||||
{
|
||||
return CopyToAsync(source, sourceLength, destination, 0, progress, default);
|
||||
}
|
||||
/// <summary>
|
||||
/// Copies a stream to another stream
|
||||
/// </summary>
|
||||
/// <param name="source">The source <see cref="Stream" /> to copy from</param>
|
||||
/// <param name="destination">The destination <see cref="Stream" /> to copy to</param>
|
||||
/// <param name="progress">An <see cref="IProgress{T}" /> implementation for reporting progress</param>
|
||||
/// <param name="cancellationToken">A cancellation token</param>
|
||||
/// <returns>A task representing the operation</returns>
|
||||
public static Task CopyToAsync(this Stream source, Stream destination, IProgress<(long, long)> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
return CopyToAsync(source, 0L, destination, 0, progress, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies a stream to another stream
|
||||
/// </summary>
|
||||
/// <param name="source">The source <see cref="Stream" /> to copy from</param>
|
||||
/// <param name="destination">The destination <see cref="Stream" /> to copy to</param>
|
||||
/// <param name="progress">An <see cref="IProgress{T}" /> implementation for reporting progress</param>
|
||||
/// <returns>A task representing the operation</returns>
|
||||
public static Task CopyToAsync(this Stream source, Stream destination, IProgress<(long, long)> progress)
|
||||
{
|
||||
return CopyToAsync(source, 0L, destination, 0, progress, default);
|
||||
}
|
||||
/// <summary>
|
||||
/// Copies a stream to another stream
|
||||
/// </summary>
|
||||
/// <param name="source">The source <see cref="Stream" /> to copy from</param>
|
||||
/// <param name="sourceLength">The length of the source stream, if known - used for progress reporting</param>
|
||||
/// <param name="destination">The destination <see cref="Stream" /> to copy to</param>
|
||||
/// <param name="progress">An <see cref="IProgress{T}" /> implementation for reporting progress</param>
|
||||
/// <returns>A task representing the operation</returns>
|
||||
public static Task CopyToAsync(this Stream source, long sourceLength, Stream destination, IProgress<(long, long)> progress)
|
||||
{
|
||||
return CopyToAsync(source, sourceLength, destination, 0, progress, default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies a stream to another stream
|
||||
/// </summary>
|
||||
/// <param name="source">The source <see cref="Stream" /> to copy from</param>
|
||||
/// <param name="destination">The destination <see cref="Stream" /> to copy to</param>
|
||||
/// <param name="progress">An <see cref="IProgress{T}" /> implementation for reporting progress</param>
|
||||
/// <returns>A task representing the operation</returns>
|
||||
public static Task CopyToAsync(this Stream source, Stream destination, IProgress<(long, long)> progress)
|
||||
{
|
||||
return CopyToAsync(source, 0L, destination, 0, progress, default);
|
||||
}
|
||||
}
|
||||
@@ -5,249 +5,250 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using Humanizer;
|
||||
|
||||
namespace Artemis.Core
|
||||
namespace Artemis.Core;
|
||||
|
||||
/// <summary>
|
||||
/// A static class providing <see cref="Type" /> extensions
|
||||
/// </summary>
|
||||
public static class TypeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// A static class providing <see cref="Type" /> extensions
|
||||
/// </summary>
|
||||
public static class TypeExtensions
|
||||
private static readonly Dictionary<Type, List<Type>> PrimitiveTypeConversions = new()
|
||||
{
|
||||
private static readonly Dictionary<Type, List<Type>> PrimitiveTypeConversions = new()
|
||||
{
|
||||
{typeof(decimal), new List<Type> {typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char)}},
|
||||
{typeof(double), new List<Type> {typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float)}},
|
||||
{typeof(float), new List<Type> {typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float)}},
|
||||
{typeof(ulong), new List<Type> {typeof(byte), typeof(ushort), typeof(uint), typeof(char)}},
|
||||
{typeof(long), new List<Type> {typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(char)}},
|
||||
{typeof(uint), new List<Type> {typeof(byte), typeof(ushort), typeof(char)}},
|
||||
{typeof(int), new List<Type> {typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(char)}},
|
||||
{typeof(ushort), new List<Type> {typeof(byte), typeof(char)}},
|
||||
{typeof(short), new List<Type> {typeof(byte)}}
|
||||
};
|
||||
{typeof(decimal), new List<Type> {typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char)}},
|
||||
{typeof(double), new List<Type> {typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float)}},
|
||||
{typeof(float), new List<Type> {typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float)}},
|
||||
{typeof(ulong), new List<Type> {typeof(byte), typeof(ushort), typeof(uint), typeof(char)}},
|
||||
{typeof(long), new List<Type> {typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(char)}},
|
||||
{typeof(uint), new List<Type> {typeof(byte), typeof(ushort), typeof(char)}},
|
||||
{typeof(int), new List<Type> {typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(char)}},
|
||||
{typeof(ushort), new List<Type> {typeof(byte), typeof(char)}},
|
||||
{typeof(short), new List<Type> {typeof(byte)}}
|
||||
};
|
||||
|
||||
private static readonly Dictionary<Type, string> TypeKeywords = new()
|
||||
{
|
||||
{typeof(bool), "bool"},
|
||||
{typeof(byte), "byte"},
|
||||
{typeof(sbyte), "sbyte"},
|
||||
{typeof(char), "char"},
|
||||
{typeof(decimal), "decimal"},
|
||||
{typeof(double), "double"},
|
||||
{typeof(float), "float"},
|
||||
{typeof(int), "int"},
|
||||
{typeof(uint), "uint"},
|
||||
{typeof(long), "long"},
|
||||
{typeof(ulong), "ulong"},
|
||||
{typeof(short), "short"},
|
||||
{typeof(ushort), "ushort"},
|
||||
{typeof(object), "object"},
|
||||
{typeof(string), "string"}
|
||||
};
|
||||
private static readonly Dictionary<Type, string> TypeKeywords = new()
|
||||
{
|
||||
{typeof(bool), "bool"},
|
||||
{typeof(byte), "byte"},
|
||||
{typeof(sbyte), "sbyte"},
|
||||
{typeof(char), "char"},
|
||||
{typeof(decimal), "decimal"},
|
||||
{typeof(double), "double"},
|
||||
{typeof(float), "float"},
|
||||
{typeof(int), "int"},
|
||||
{typeof(uint), "uint"},
|
||||
{typeof(long), "long"},
|
||||
{typeof(ulong), "ulong"},
|
||||
{typeof(short), "short"},
|
||||
{typeof(ushort), "ushort"},
|
||||
{typeof(object), "object"},
|
||||
{typeof(string), "string"}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the provided type is of a specified generic type
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check</param>
|
||||
/// <param name="genericType">The generic type to match</param>
|
||||
/// <returns>True if the <paramref name="type" /> is generic and of generic type <paramref name="genericType" /></returns>
|
||||
public static bool IsGenericType(this Type? type, Type genericType)
|
||||
{
|
||||
if (type == null)
|
||||
return false;
|
||||
/// <summary>
|
||||
/// Determines whether the provided type is of a specified generic type
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check</param>
|
||||
/// <param name="genericType">The generic type to match</param>
|
||||
/// <returns>True if the <paramref name="type" /> is generic and of generic type <paramref name="genericType" /></returns>
|
||||
public static bool IsGenericType(this Type? type, Type genericType)
|
||||
{
|
||||
if (type == null)
|
||||
return false;
|
||||
|
||||
return type.BaseType?.GetGenericTypeDefinition() == genericType;
|
||||
}
|
||||
return type.BaseType?.GetGenericTypeDefinition() == genericType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the provided type is a struct
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check</param>
|
||||
/// <returns><see langword="true" /> if the type is a struct, otherwise <see langword="false" /></returns>
|
||||
public static bool IsStruct(this Type type)
|
||||
{
|
||||
return type.IsValueType && !type.IsPrimitive && !type.IsEnum;
|
||||
}
|
||||
/// <summary>
|
||||
/// Determines whether the provided type is a struct
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check</param>
|
||||
/// <returns><see langword="true" /> if the type is a struct, otherwise <see langword="false" /></returns>
|
||||
public static bool IsStruct(this Type type)
|
||||
{
|
||||
return type.IsValueType && !type.IsPrimitive && !type.IsEnum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the provided type is any kind of numeric type
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check</param>
|
||||
/// <returns><see langword="true" /> if the type a numeric type, otherwise <see langword="false" /></returns>
|
||||
public static bool TypeIsNumber(this Type type)
|
||||
{
|
||||
return type == typeof(sbyte)
|
||||
|| type == typeof(byte)
|
||||
|| type == typeof(short)
|
||||
|| type == typeof(ushort)
|
||||
|| type == typeof(int)
|
||||
|| type == typeof(uint)
|
||||
|| type == typeof(long)
|
||||
|| type == typeof(ulong)
|
||||
|| type == typeof(float)
|
||||
|| type == typeof(double)
|
||||
|| type == typeof(decimal);
|
||||
}
|
||||
/// <summary>
|
||||
/// Determines whether the provided type is any kind of numeric type
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check</param>
|
||||
/// <returns><see langword="true" /> if the type a numeric type, otherwise <see langword="false" /></returns>
|
||||
public static bool TypeIsNumber(this Type type)
|
||||
{
|
||||
return type == typeof(sbyte)
|
||||
|| type == typeof(byte)
|
||||
|| type == typeof(short)
|
||||
|| type == typeof(ushort)
|
||||
|| type == typeof(int)
|
||||
|| type == typeof(uint)
|
||||
|| type == typeof(long)
|
||||
|| type == typeof(ulong)
|
||||
|| type == typeof(float)
|
||||
|| type == typeof(double)
|
||||
|| type == typeof(decimal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the provided value is any kind of numeric type
|
||||
/// </summary>
|
||||
/// <param name="value">The value to check</param>
|
||||
/// <returns><see langword="true" /> if the value is of a numeric type, otherwise <see langword="false" /></returns>
|
||||
public static bool IsNumber([NotNullWhenAttribute(true)] this object? value)
|
||||
{
|
||||
return value is sbyte or byte or short or ushort or int or uint or long or ulong or float or double or decimal;
|
||||
}
|
||||
/// <summary>
|
||||
/// Determines whether the provided value is any kind of numeric type
|
||||
/// </summary>
|
||||
/// <param name="value">The value to check</param>
|
||||
/// <returns><see langword="true" /> if the value is of a numeric type, otherwise <see langword="false" /></returns>
|
||||
public static bool IsNumber([NotNullWhenAttribute(true)] this object? value)
|
||||
{
|
||||
return value is sbyte or byte or short or ushort or int or uint or long or ulong or float or double or decimal;
|
||||
}
|
||||
|
||||
// From https://stackoverflow.com/a/2224421/5015269 but inverted and renamed to match similar framework methods
|
||||
/// <summary>
|
||||
/// Determines whether an instance of a specified type can be casted to a variable of the current type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool IsCastableFrom(this Type to, Type from)
|
||||
{
|
||||
if (to.TypeIsNumber() && from.TypeIsNumber())
|
||||
return true;
|
||||
if (to.IsAssignableFrom(from))
|
||||
return true;
|
||||
if (PrimitiveTypeConversions.ContainsKey(to) && PrimitiveTypeConversions[to].Contains(from))
|
||||
return true;
|
||||
bool castable = from.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||
.Any(m => m.ReturnType == to && (m.Name == "op_Implicit" || m.Name == "op_Explicit"));
|
||||
return castable;
|
||||
}
|
||||
// From https://stackoverflow.com/a/2224421/5015269 but inverted and renamed to match similar framework methods
|
||||
/// <summary>
|
||||
/// Determines whether an instance of a specified type can be casted to a variable of the current type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool IsCastableFrom(this Type to, Type from)
|
||||
{
|
||||
if (to.TypeIsNumber() && from.TypeIsNumber())
|
||||
return true;
|
||||
if (to.IsAssignableFrom(from))
|
||||
return true;
|
||||
if (PrimitiveTypeConversions.ContainsKey(to) && PrimitiveTypeConversions[to].Contains(from))
|
||||
return true;
|
||||
bool castable = from.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||
.Any(m => m.ReturnType == to && (m.Name == "op_Implicit" || m.Name == "op_Explicit"));
|
||||
return castable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scores how well the two types can be casted from one to another, 5 being a perfect match and 0 being not castable
|
||||
/// at all
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static int ScoreCastability(this Type to, Type? from)
|
||||
{
|
||||
if (from == null)
|
||||
return 0;
|
||||
|
||||
if (to == from)
|
||||
return 5;
|
||||
if (to.TypeIsNumber() && from.TypeIsNumber())
|
||||
return 4;
|
||||
if (PrimitiveTypeConversions.ContainsKey(to) && PrimitiveTypeConversions[to].Contains(from))
|
||||
return 3;
|
||||
bool castable = from.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||
.Any(m => m.ReturnType == to && (m.Name == "op_Implicit" || m.Name == "op_Explicit"));
|
||||
if (castable)
|
||||
return 2;
|
||||
if (to.IsAssignableFrom(from))
|
||||
return 1;
|
||||
/// <summary>
|
||||
/// Scores how well the two types can be casted from one to another, 5 being a perfect match and 0 being not castable
|
||||
/// at all
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static int ScoreCastability(this Type to, Type? from)
|
||||
{
|
||||
if (from == null)
|
||||
return 0;
|
||||
|
||||
if (to == from)
|
||||
return 5;
|
||||
if (to.TypeIsNumber() && from.TypeIsNumber())
|
||||
return 4;
|
||||
if (PrimitiveTypeConversions.ContainsKey(to) && PrimitiveTypeConversions[to].Contains(from))
|
||||
return 3;
|
||||
bool castable = from.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||
.Any(m => m.ReturnType == to && (m.Name == "op_Implicit" || m.Name == "op_Explicit"));
|
||||
if (castable)
|
||||
return 2;
|
||||
if (to.IsAssignableFrom(from))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the default value of the given type
|
||||
/// </summary>
|
||||
public static object? GetDefault(this Type type)
|
||||
{
|
||||
return type.IsValueType ? Activator.CreateInstance(type) : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given type is a generic enumerable
|
||||
/// </summary>
|
||||
public static bool IsGenericEnumerable(this Type type)
|
||||
{
|
||||
// String is an IEnumerable to be fair, but not for us
|
||||
if (type == typeof(string))
|
||||
return false;
|
||||
// It may actually be one instead of implementing one ;)
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
|
||||
return true;
|
||||
|
||||
return type.GetInterfaces().Any(x =>
|
||||
x.IsGenericType &&
|
||||
x.GetGenericTypeDefinition() == typeof(IEnumerable<>));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the type of the provided generic enumerable type
|
||||
/// </summary>
|
||||
public static Type? GetGenericEnumerableType(this Type type)
|
||||
{
|
||||
// It may actually be one instead of implementing one ;)
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
|
||||
return type.GenericTypeArguments[0];
|
||||
|
||||
Type? enumerableType = type.GetInterfaces().FirstOrDefault(x =>
|
||||
x.IsGenericType &&
|
||||
x.GetGenericTypeDefinition() == typeof(IEnumerable<>));
|
||||
|
||||
return enumerableType?.GenericTypeArguments[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the <paramref name="typeToCheck"></paramref> is of a certain <paramref name="genericType" />.
|
||||
/// </summary>
|
||||
/// <param name="typeToCheck">The type to check.</param>
|
||||
/// <param name="genericType">The generic type it should be or implement</param>
|
||||
public static bool IsOfGenericType(this Type typeToCheck, Type genericType)
|
||||
{
|
||||
return typeToCheck.IsOfGenericType(genericType, out Type? _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines a display name for the given type
|
||||
/// </summary>
|
||||
/// <param name="type">The type to determine the name for</param>
|
||||
/// <param name="humanize">Whether or not to humanize the result, defaults to false</param>
|
||||
/// <returns></returns>
|
||||
public static string GetDisplayName(this Type type, bool humanize = false)
|
||||
{
|
||||
if (!type.IsGenericType)
|
||||
{
|
||||
string displayValue = TypeKeywords.TryGetValue(type, out string? keyword) ? keyword! : type.Name;
|
||||
return humanize ? displayValue.Humanize() : displayValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the default value of the given type
|
||||
/// </summary>
|
||||
public static object? GetDefault(this Type type)
|
||||
{
|
||||
return type.IsValueType ? Activator.CreateInstance(type) : null;
|
||||
}
|
||||
Type genericTypeDefinition = type.GetGenericTypeDefinition();
|
||||
if (genericTypeDefinition == typeof(Nullable<>))
|
||||
return type.GenericTypeArguments[0].GetDisplayName(humanize) + "?";
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given type is a generic enumerable
|
||||
/// </summary>
|
||||
public static bool IsGenericEnumerable(this Type type)
|
||||
string stripped = genericTypeDefinition.Name.Split('`')[0];
|
||||
return $"{stripped}<{string.Join(", ", type.GenericTypeArguments.Select(t => t.GetDisplayName(humanize)))}>";
|
||||
}
|
||||
|
||||
private static bool IsOfGenericType(this Type? typeToCheck, Type genericType, out Type? concreteGenericType)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// String is an IEnumerable to be fair, but not for us
|
||||
if (type == typeof(string))
|
||||
concreteGenericType = null;
|
||||
|
||||
if (genericType == null)
|
||||
throw new ArgumentNullException(nameof(genericType));
|
||||
|
||||
if (!genericType.IsGenericTypeDefinition)
|
||||
throw new ArgumentException("The definition needs to be a GenericTypeDefinition", nameof(genericType));
|
||||
|
||||
if (typeToCheck == null || typeToCheck == typeof(object))
|
||||
return false;
|
||||
// It may actually be one instead of implementing one ;)
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
|
||||
|
||||
if (typeToCheck == genericType)
|
||||
{
|
||||
concreteGenericType = typeToCheck;
|
||||
return true;
|
||||
|
||||
return type.GetInterfaces().Any(x =>
|
||||
x.IsGenericType &&
|
||||
x.GetGenericTypeDefinition() == typeof(IEnumerable<>));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the type of the provided generic enumerable type
|
||||
/// </summary>
|
||||
public static Type? GetGenericEnumerableType(this Type type)
|
||||
{
|
||||
// It may actually be one instead of implementing one ;)
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
|
||||
return type.GenericTypeArguments[0];
|
||||
|
||||
Type? enumerableType = type.GetInterfaces().FirstOrDefault(x =>
|
||||
x.IsGenericType &&
|
||||
x.GetGenericTypeDefinition() == typeof(IEnumerable<>));
|
||||
|
||||
return enumerableType?.GenericTypeArguments[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the <paramref name="typeToCheck"></paramref> is of a certain <paramref name="genericType"/>.
|
||||
/// </summary>
|
||||
/// <param name="typeToCheck">The type to check.</param>
|
||||
/// <param name="genericType">The generic type it should be or implement</param>
|
||||
public static bool IsOfGenericType(this Type typeToCheck, Type genericType)
|
||||
{
|
||||
return typeToCheck.IsOfGenericType(genericType, out Type? _);
|
||||
}
|
||||
|
||||
private static bool IsOfGenericType(this Type? typeToCheck, Type genericType, out Type? concreteGenericType)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
concreteGenericType = null;
|
||||
|
||||
if (genericType == null)
|
||||
throw new ArgumentNullException(nameof(genericType));
|
||||
|
||||
if (!genericType.IsGenericTypeDefinition)
|
||||
throw new ArgumentException("The definition needs to be a GenericTypeDefinition", nameof(genericType));
|
||||
|
||||
if (typeToCheck == null || typeToCheck == typeof(object))
|
||||
return false;
|
||||
|
||||
if (typeToCheck == genericType)
|
||||
{
|
||||
concreteGenericType = typeToCheck;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((typeToCheck.IsGenericType ? typeToCheck.GetGenericTypeDefinition() : typeToCheck) == genericType)
|
||||
{
|
||||
concreteGenericType = typeToCheck;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (genericType.IsInterface)
|
||||
foreach (Type i in typeToCheck.GetInterfaces())
|
||||
if (i.IsOfGenericType(genericType, out concreteGenericType))
|
||||
return true;
|
||||
|
||||
typeToCheck = typeToCheck.BaseType;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines a display name for the given type
|
||||
/// </summary>
|
||||
/// <param name="type">The type to determine the name for</param>
|
||||
/// <param name="humanize">Whether or not to humanize the result, defaults to false</param>
|
||||
/// <returns></returns>
|
||||
public static string GetDisplayName(this Type type, bool humanize = false)
|
||||
{
|
||||
if (!type.IsGenericType)
|
||||
{
|
||||
string displayValue = TypeKeywords.TryGetValue(type, out string? keyword) ? keyword! : type.Name;
|
||||
return humanize ? displayValue.Humanize() : displayValue;
|
||||
}
|
||||
|
||||
Type genericTypeDefinition = type.GetGenericTypeDefinition();
|
||||
if (genericTypeDefinition == typeof(Nullable<>))
|
||||
return type.GenericTypeArguments[0].GetDisplayName(humanize) + "?";
|
||||
if ((typeToCheck.IsGenericType ? typeToCheck.GetGenericTypeDefinition() : typeToCheck) == genericType)
|
||||
{
|
||||
concreteGenericType = typeToCheck;
|
||||
return true;
|
||||
}
|
||||
|
||||
string stripped = genericTypeDefinition.Name.Split('`')[0];
|
||||
return $"{stripped}<{string.Join(", ", type.GenericTypeArguments.Select(t => t.GetDisplayName(humanize)))}>";
|
||||
if (genericType.IsInterface)
|
||||
foreach (Type i in typeToCheck.GetInterfaces())
|
||||
{
|
||||
if (i.IsOfGenericType(genericType, out concreteGenericType))
|
||||
return true;
|
||||
}
|
||||
|
||||
typeToCheck = typeToCheck.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user