Skip to content

Commit

Permalink
v9.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel Abraham committed Dec 6, 2024
1 parent 223c198 commit a098db9
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 76 deletions.
19 changes: 16 additions & 3 deletions src/TypeCache/Attributes/MapAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
// Copyright (c) 2021 Samuel Abraham

using TypeCache.Extensions;

namespace TypeCache.Attributes;

public abstract class MapAttribute(Type type, string property) : Attribute
public abstract class MapAttribute : Attribute
{
public string Property { get; } = property;
public MapAttribute(Type type, string property)
{
var propertyInfo = type.GetPropertyInfo(property, true);
propertyInfo.ThrowIfNull();
propertyInfo.CanWrite.ThrowIfFalse();
propertyInfo.SetMethod?.IsStatic.ThrowIfTrue();

this.Property = property;
this.Type = type;
}

public string Property { get; }

public Type Type { get; } = type;
public Type Type { get; }
}

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
Expand Down
70 changes: 38 additions & 32 deletions src/TypeCache/Extensions/ReflectionExtensions.Type.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,47 +141,43 @@ public static CollectionType GetCollectionType(this Type @this)
public static ObjectType GetObjectType(this Type @this)
=> TypeStore.ObjectTypes[@this.IsGenericType ? @this.GetGenericTypeDefinition().TypeHandle : @this.TypeHandle];

/// <inheritdoc cref="Type.GetField(string, BindingFlags)"/>
public static FieldInfo? GetFieldInfo(this Type @this, string name, bool ignoreCase = true)
{
name.ThrowIfBlank();

var binding = INSTANCE_BINDING_FLAGS;
if (ignoreCase)
binding |= BindingFlags.IgnoreCase;

return @this.GetField(name, binding | BindingFlags.DeclaredOnly) ?? @this.GetField(name, binding);
var binding = ignoreCase ? INSTANCE_BINDING_FLAGS | BindingFlags.IgnoreCase : INSTANCE_BINDING_FLAGS;
return @this.GetField(name, binding);
}

/// <inheritdoc cref="Type.GetProperty(string, BindingFlags)"/>
public static PropertyInfo? GetPropertyInfo(this Type @this, string name, bool ignoreCase = true)
{
name.ThrowIfBlank();

var binding = INSTANCE_BINDING_FLAGS;
if (ignoreCase)
binding |= BindingFlags.IgnoreCase;

return @this.GetProperty(name, binding | BindingFlags.DeclaredOnly) ?? @this.GetProperty(name, binding);
var binding = ignoreCase ? INSTANCE_BINDING_FLAGS | BindingFlags.IgnoreCase : INSTANCE_BINDING_FLAGS;
return @this.GetProperty(name, binding);
}

/// <inheritdoc cref="Type.GetFields(BindingFlags)"/>
/// <remarks>
/// <c>=&gt; @<paramref name="this"/>.GetFields(<see cref="FlattenHierarchy"/> | <see cref="Public"/> | <see cref="Instance"/>);</c>
/// <c>=&gt; @<paramref name="this"/>.GetFields(<see cref="Public"/> | <see cref="Instance"/>);</c>
/// </remarks>
[MethodImpl(AggressiveInlining), DebuggerHidden]
public static FieldInfo[] GetPublicFields(this Type @this)
=> @this.GetFields(FlattenHierarchy | Public | Instance);
=> @this.GetFields(Public | Instance);

/// <inheritdoc cref="Type.GetMethods(BindingFlags)"/>
/// <remarks>
/// <c>=&gt; @<paramref name="this"/>.GetMethods(<see cref="FlattenHierarchy"/> | <see cref="Public"/> | <see cref="Instance"/>);</c>
/// <c>=&gt; @<paramref name="this"/>.GetMethods(<see cref="Public"/> | <see cref="Instance"/>);</c>
/// </remarks>
[MethodImpl(AggressiveInlining), DebuggerHidden]
public static MethodInfo[] GetPublicMethods(this Type @this)
=> @this.GetMethods(FlattenHierarchy | Public | Instance);
=> @this.GetMethods(Public | Instance);

/// <inheritdoc cref="Type.GetProperties(BindingFlags)"/>
public static PropertyInfo[] GetPublicProperties(this Type @this)
=> @this.GetProperties(FlattenHierarchy | Public | Instance)
=> @this.GetProperties(Public | Instance)
.Where(propertyInfo => propertyInfo.GetMethod?.IsStatic is not true && propertyInfo.SetMethod?.IsStatic is not true)
.ToArray();

Expand Down Expand Up @@ -249,18 +245,28 @@ _ when TypeStore.DataTypes.TryGetValue(@this.TypeHandle, out var scalarType) =>
=> @this.GetStaticPropertyInfo(name)?.GetStaticValue(index);

/// <summary>
/// Gets the C# name of a type. For example: <c>IDictionary&lt;String, List&lt;Int32&gt;&gt;</c>.
/// Gets the C# name of a type. For example:
/// <list type="bullet">
/// <item><c>UInt64</c></item>
/// <item><c>String</c></item>
/// <item><c>Char*</c></item>
/// <item><c>Int32[]</c></item>
/// <item><c>Int32[][][]</c></item>
/// <item><c>IList&lt;Boolean&gt;</c></item>
/// <item><c>IDictionary&lt;String, List&lt;Int32&gt;&gt;</c></item>
/// <item><c>IDictionary&lt;,&gt;</c></item>
/// </list>
/// </summary>
public static string GetTypeName(this Type type)
{
var typeName = type.Name;
if (!type.IsGenericType)
return typeName;

typeName = typeName[0..typeName.IndexOf(GENERIC_TICKMARK)];
var genericTypeNameCSV = string.Join(", ", type.GetGenericArguments().Select(_ => _.GetTypeName()));
return $"{typeName}<{genericTypeNameCSV}>";
}
=> type switch
{
_ when type == typeof(string) => type.Name,
{ IsPointer: true } => Invariant($"{type.GetElementType()!.GetTypeName()}*"),
{ IsArray: true } => Invariant($"{type.Name}{string.Join(string.Empty, "[]".Repeat(type.GetArrayRank() - 1))}"),
{ IsGenericTypeDefinition: true } => Invariant($"{type.Name[0..type.Name.IndexOf(GENERIC_TICKMARK)]}<{string.Join(string.Empty, ','.Repeat(type.GetGenericArguments().Length - 1))}>"),
{ IsGenericType: true } => Invariant($"{type.Name[0..type.Name.IndexOf(GENERIC_TICKMARK)]}<{string.Join(", ", type.GetGenericArguments().Select(_ => _.GetTypeName()))}>"),
_ => type.Name
};

public static bool Implements(this Type @this, Type type)
{
Expand All @@ -275,15 +281,15 @@ _ when @this.Is(type) => true,

static bool isDescendantOf(Type? baseType, Type type)
{
while (baseType is not null)
{
if (baseType.Is(type))
return true;
Loop:
if (baseType is null)
return false;

baseType = baseType.BaseType;
}
if (baseType.Is(type))
return true;

return false;
baseType = baseType.BaseType;
goto Loop;
}
}

Expand Down
87 changes: 51 additions & 36 deletions src/TypeCache/Extensions/ThrowIfExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright (c) 2021 Samuel Abraham

using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace TypeCache.Extensions;
Expand All @@ -8,6 +10,15 @@ public static class ThrowIfExtensions
{
private const string NULL = "null";

/// <exception cref="ArgumentOutOfRangeException"/>
public static void ThrowIfNot<T>(this object @this, string? message = null,
[CallerMemberName] string? caller = null,
[CallerArgumentExpression("this")] string? argument = null)
=> (@this is not T).ThrowIfFalse(
message: message ?? Invariant($"{caller}: {nameof(ThrowIfNot)}<{typeof(T).GetTypeName()}>({argument})."),
caller: caller,
argument: argument);

/// <param name="caller">Leave null- value will be injected</param>
/// <param name="argument">Leave null- value will be injected</param>
/// <exception cref="ArgumentOutOfRangeException"/>
Expand All @@ -17,47 +28,49 @@ public static void ThrowIfNotEqual<T>(this T? @this, T? value, IEqualityComparer
{
comparer ??= EqualityComparer<T>.Default;

if (!comparer.Equals(@this, value))
throw new ArgumentOutOfRangeException(argument, message ?? Invariant($"{caller}: {@this?.ToString() ?? NULL}.{nameof(ThrowIfNotEqual)}<{typeof(T).Name}>({argument})."));
comparer.Equals(@this, value).ThrowIfFalse(
message: message ?? Invariant($"{caller}: {@this?.ToString() ?? NULL}.{nameof(ThrowIfNotEqual)}<{typeof(T).Name}>({argument})."),
caller: caller,
argument: argument);
}

/// <exception cref="ArgumentOutOfRangeException"/>
public static void ThrowIfNotEqualIgnoreCase(this string? @this, string? value, string? message = null,
[CallerMemberName] string? caller = null,
[CallerArgumentExpression("this")] string? argument = null)
{
if (!string.Equals(@this, value, StringComparison.OrdinalIgnoreCase))
throw new ArgumentOutOfRangeException(argument, message ?? Invariant($"{caller}: {@this ?? NULL}.{nameof(ThrowIfNotEqualIgnoreCase)}({argument})."));
}
=> string.Equals(@this, value, StringComparison.OrdinalIgnoreCase).ThrowIfFalse(
message: message ?? Invariant($"{caller}: {@this ?? NULL}.{nameof(ThrowIfNotEqualIgnoreCase)}({argument})."),
caller: caller,
argument: argument);

/// <exception cref="ArgumentOutOfRangeException"/>
public static void ThrowIfBlank([NotNull] this string? @this, string? message = null,
[CallerMemberName] string? caller = null,
[CallerArgumentExpression("this")] string? argument = null)
{
if (@this.IsBlank())
throw new ArgumentOutOfRangeException(argument, message ?? Invariant($"{caller}: {argument}.{nameof(ThrowIfBlank)}()."));
}
=> @this.IsBlank().ThrowIfTrue(
message: message ?? Invariant($"{caller}: {argument}.{nameof(ThrowIfBlank)}()."),
caller: caller,
argument: argument);

/// <exception cref="ArgumentOutOfRangeException"/>
public static void ThrowIfEmpty<T>([NotNull] this IEnumerable<T> @this, string? message = null,
[CallerMemberName] string? caller = null,
[CallerArgumentExpression("this")] string? argument = null)
where T : notnull
{
if (!@this.Any())
throw new ArgumentOutOfRangeException(argument, message ?? Invariant($"{caller}: {argument}.{nameof(ThrowIfEmpty)}<IEnumerable<{typeof(T).Name}>>()."));
}
=> @this.Any().ThrowIfFalse(
message: message ?? Invariant($"{caller}: {argument}.{nameof(ThrowIfEmpty)}<{typeof(T).GetTypeName()}>()."),
caller: caller,
argument: argument);

/// <exception cref="ArgumentOutOfRangeException"/>
public static void ThrowIfEmpty<T>([NotNull] this T[] @this, string? message = null,
[CallerMemberName] string? caller = null,
[CallerArgumentExpression("this")] string? argument = null)
where T : notnull
{
if (@this.Length is 0)
throw new ArgumentOutOfRangeException(argument, message ?? Invariant($"{caller}: {argument}.{nameof(ThrowIfEmpty)}<{typeof(T).Name}[]>()."));
}
=> (@this.Length is 0).ThrowIfTrue(
message: message ?? Invariant($"{caller}: {argument}.{nameof(ThrowIfEmpty)}<{typeof(T).Name}[]>()."),
caller: caller,
argument: argument);

/// <exception cref="ArgumentOutOfRangeException"/>
public static void ThrowIfEqual<T>(this T? @this, T? value, IEqualityComparer<T>? comparer = null, string? message = null,
Expand All @@ -66,18 +79,20 @@ public static void ThrowIfEqual<T>(this T? @this, T? value, IEqualityComparer<T>
{
comparer ??= EqualityComparer<T>.Default;

if (comparer.Equals(@this, value))
throw new ArgumentOutOfRangeException(argument, message ?? Invariant($"{caller}: {@this?.ToString() ?? NULL}.{nameof(ThrowIfEqual)}<{typeof(T).Name}>({argument})."));
comparer.Equals(@this, value).ThrowIfTrue(
message: message ?? Invariant($"{caller}: {@this?.ToString() ?? NULL}.{nameof(ThrowIfEqual)}<{typeof(T).Name}>({argument})."),
caller: caller,
argument: argument);
}

/// <exception cref="ArgumentOutOfRangeException"/>
public static void ThrowIfEqualIgnoreCase(this string? @this, string? value, string? message = null,
[CallerMemberName] string? caller = null,
[CallerArgumentExpression("this")] string? argument = null)
{
if (string.Equals(@this, value, StringComparison.OrdinalIgnoreCase))
throw new ArgumentOutOfRangeException(argument, message ?? Invariant($"{caller}: {@this ?? NULL}.{nameof(ThrowIfEqualIgnoreCase)}({argument})."));
}
=> string.Equals(@this, value, StringComparison.OrdinalIgnoreCase).ThrowIfTrue(
message: message ?? Invariant($"{caller}: {@this?.ToString() ?? NULL}.{nameof(ThrowIfEqualIgnoreCase)}({argument})."),
caller: caller,
argument: argument);

/// <exception cref="ArgumentOutOfRangeException"/>
public static void ThrowIfFalse(this bool @this, string? message = null,
Expand All @@ -98,29 +113,29 @@ public static void ThrowIfNull<T>([NotNull] this T? @this, string? message = nul
public static void ThrowIfSame<T>(this T? @this, T? value, StringComparison comparison = StringComparison.OrdinalIgnoreCase, string? message = null,
[CallerMemberName] string? caller = null,
[CallerArgumentExpression("this")] string? argument = null)
{
if (object.ReferenceEquals(@this, value))
throw new ArgumentOutOfRangeException(argument, message ?? Invariant($"{caller}: {@this?.ToString() ?? NULL}.{nameof(ThrowIfSame)}({value?.ToString() ?? NULL})."));
}
=> object.ReferenceEquals(@this, value).ThrowIfTrue(
message: message ?? Invariant($"{caller}: {@this?.ToString() ?? NULL}.{nameof(ThrowIfSame)}({value?.ToString() ?? NULL})."),
caller: caller,
argument: argument);

/// <exception cref="ArgumentOutOfRangeException"/>
public static void ThrowIfSame<T>(this (T?, T?) @this, string? message = null,
[CallerMemberName] string? caller = null,
[CallerArgumentExpression("this")] string? argument = null)
where T : notnull
{
if (object.ReferenceEquals(@this.Item1, @this.Item2))
throw new ArgumentOutOfRangeException(argument, message ?? Invariant($"{caller}: {@this.Item1?.ToString() ?? NULL}.{nameof(ThrowIfSame)}<{typeof(T).Name}>({@this.Item2?.ToString() ?? NULL})."));
}
=> object.ReferenceEquals(@this.Item1, @this.Item2).ThrowIfTrue(
message: message ?? Invariant($"{caller}: {@this.Item1?.ToString() ?? NULL}.{nameof(ThrowIfSame)}<{typeof(T).Name}>({@this.Item2?.ToString() ?? NULL})."),
caller: caller,
argument: argument);

/// <exception cref="ArgumentOutOfRangeException"/>
public static void ThrowIfNotSame(this object? @this, object? value, string? message = null,
[CallerMemberName] string? caller = null,
[CallerArgumentExpression("this")] string? argument = null)
{
if (object.ReferenceEquals(@this, value))
throw new ArgumentOutOfRangeException(argument, message ?? Invariant($"{caller}: {@this?.ToString() ?? NULL}.{nameof(ThrowIfNotSame)}({value?.ToString() ?? NULL})."));
}
=> object.ReferenceEquals(@this, value).ThrowIfFalse(
message: message ?? Invariant($"{caller}: {@this?.ToString() ?? NULL}.{nameof(ThrowIfNotSame)}({value?.ToString() ?? NULL})."),
caller: caller,
argument: argument);

/// <exception cref="ArgumentOutOfRangeException"/>
public static void ThrowIfTrue(this bool @this, string? message = null,
Expand Down
2 changes: 1 addition & 1 deletion src/TypeCache/TypeCache.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<RootNamespace>TypeCache</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PackageId>TypeCache</PackageId>
<Version>9.0.0</Version>
<Version>9.0.1</Version>
<Authors>Samuel Abraham &lt;sam987883@gmail.com&gt;</Authors>
<Company>Samuel Abraham &lt;sam987883@gmail.com&gt;</Company>
<Title>TypeCache Reflection</Title>
Expand Down
23 changes: 19 additions & 4 deletions tests/TypeCache.Tests/Extensions/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -450,12 +450,27 @@ public void Type_Create()
Assert.NotNull(typeof(IndexOutOfRangeException).Create());
}

[Fact]
public void Type_GetTypeName()
[Theory]
[InlineData("UInt64", typeof(ulong))]
[InlineData("String", typeof(string))]
[InlineData("Char*", typeof(char*))]
[InlineData("Int32[]", typeof(int[]))]
[InlineData("Int32[][][]", typeof(int[][][]))]
[InlineData("IList<Boolean>", typeof(IList<bool>))]
[InlineData("IDictionary<String, List<Int32>>", typeof(IDictionary<string, List<int>>))]
public void Type_GetTypeName(string expected, Type type)
{
const string expected = "IDictionary<String, List<Int32>>";
var actual = typeof(IDictionary<string, List<int>>).GetTypeName();
var actual = type.GetTypeName();

Assert.Equal(expected, actual);

if (type.IsGenericType)
{
type.GenericTypeArguments.ForEach(_ => expected = expected.Replace(_.GetTypeName(), string.Empty));
expected = expected.Replace(" ", string.Empty);
actual = type.GetGenericTypeDefinition().GetTypeName();

Assert.Equal(expected, actual);
}
}
}
4 changes: 4 additions & 0 deletions tests/TypeCache.Tests/TypeCache.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@
<ProjectReference Include="..\..\src\TypeCache\TypeCache.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Attributes\" />
</ItemGroup>

</Project>

0 comments on commit a098db9

Please sign in to comment.