Skip to content

Commit cbef155

Browse files
author
dahall
committed
Added many extension methods for Type, most useful for marshaling.
1 parent c53adc7 commit cbef155

File tree

1 file changed

+99
-44
lines changed

1 file changed

+99
-44
lines changed

Core/Extensions/ReflectionExtensions.cs

Lines changed: 99 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ public static class ReflectionExtensions
1212
{
1313
private const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase;
1414

15+
/// <summary>Gets the alignment of a type.</summary>
16+
public static int AlignOf<T>() where T : unmanaged { unsafe { return sizeof(AlignOfHelper<T>) - sizeof(T); } }
17+
18+
/// <summary>Retrieves the non-<see cref="Nullable{T}"/> type for the provided type.</summary>
19+
/// <param name="type">The type.</param>
20+
/// <returns>If the type is <see cref="Nullable{T}"/>, returns <c>T</c>; otherwise, returns <paramref name="type"/>.</returns>
21+
public static Type AsNonNullable(this Type type) => type.IsNullable() ? type.GetGenericArguments()[0] : type;
22+
1523
/// <summary>Converts the given value object to the specified type.</summary>
1624
/// <param name="value">The <see cref="object"/> to convert.</param>
1725
/// <param name="type">The <see cref="Type"/> to conver the <paramref name="value"/> paramter to.</param>
@@ -45,14 +53,14 @@ public static class ReflectionExtensions
4553
catch
4654
{
4755
// See if type has conversion constructor
48-
var ci = type.GetConstructor(new[] { value.GetType() });
56+
var ci = type.GetConstructor([value.GetType()]);
4957
if (ci is not null)
50-
return ci.Invoke(new[] { value });
58+
return ci.Invoke([value]);
5159

5260
// See if type has static conversion method
53-
var mi = type.GetMethod("op_Implicit", new[] { value.GetType() }) ?? type.GetMethod("op_Explicit", new[] { value.GetType() });
61+
var mi = type.GetMethod("op_Implicit", [value.GetType()]) ?? type.GetMethod("op_Explicit", [value.GetType()]);
5462
if (mi is not null)
55-
return mi.Invoke(null, new[] { value });
63+
return mi.Invoke(null, [value]);
5664

5765
// If both value and type are value types, and type is smaller, try to do binary conversion
5866
try
@@ -81,6 +89,13 @@ public static IEnumerable<Type> EnumInheritance(this Type type)
8189
yield return type = type.BaseType;
8290
}
8391

92+
/// <summary>Gets the alignment of the type.</summary>
93+
public static int GetAlignment(this Type type) =>
94+
(int)typeof(ReflectionExtensions).GetMethod(nameof(AlignOf), Type.EmptyTypes)!.MakeGenericMethod(type).Invoke(null, null)!;
95+
96+
/// <summary>Gets the number of bits in the type's blitted value.</summary>
97+
public static int GetBitSize(this Type type) { unsafe { return Marshal.SizeOf(type) * 8; } }
98+
8499
/// <summary>Searches for the constants defined for <paramref name="type"/>, using the specified binding constraints.</summary>
85100
/// <param name="type">The type to search.</param>
86101
/// <param name="bindingFlags">A bitwise combination of the enumeration values that specify how the search is conducted.</param>
@@ -234,6 +249,37 @@ public static void InvokeMethod(this object? obj, string methodName, Type[] argT
234249
return (T?)mi.Invoke(obj, args);
235250
}
236251

252+
/// <summary>Gets a value that determines if the type is an array that can be blitted.</summary>
253+
public static bool IsBlittableArray(this Type? type)
254+
{
255+
if (type is null || !type.IsArray || type.GetArrayRank() != 1)
256+
return false;
257+
while (type is not null && type.IsArray) type = type.GetElementType();
258+
return type.IsBlittablePrimitive();
259+
}
260+
261+
/// <summary>Gets a value that determines if the field is a blittable type.</summary>
262+
public static bool IsBlittableField(this FieldInfo fieldInfo) => fieldInfo.FieldType.IsBlittablePrimitive() || fieldInfo.FieldType == typeof(bool) || fieldInfo.FieldType.IsEnum || fieldInfo.FieldType.IsBlittableStruct();
263+
264+
/// <summary>Gets a value that determines if the type is a primitive type that can be blitted.</summary>
265+
public static bool IsBlittablePrimitive(this Type? type) => type is not null && (IsIntegral(type) || IsNativeSized(type) || IsFloatingPoint(type));
266+
267+
/// <summary>Gets a value that determines if the type is a floating point type.</summary>
268+
public static bool IsFloatingPoint(this Type? type) => Type.GetTypeCode(type) is TypeCode.Single or TypeCode.Double;
269+
270+
/// <summary>Gets a value that determines if the type is an integral type.</summary>
271+
public static bool IsIntegral(this Type? type) => Type.GetTypeCode(type) is >= TypeCode.SByte and <= TypeCode.UInt64;
272+
273+
/// <summary>Gets a value that determines if the type is <see cref="IntPtr"/> or <see cref="UIntPtr"/>.</summary>
274+
public static bool IsNativeSized(this Type? type) => type == typeof(IntPtr) || type == typeof(UIntPtr);
275+
276+
/// <summary>Gets a value that determines if this integer is a factorial of 2.</summary>
277+
public static bool IsPow2(this int value)
278+
{
279+
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Value must be positive.");
280+
return value != 0 && (value & (value - 1)) == 0;
281+
}
282+
237283
/// <summary>Sets a named field on an object.</summary>
238284
/// <typeparam name="T">The type of the field to be set.</typeparam>
239285
/// <typeparam name="TS">The type of the object on which to the set the field.</typeparam>
@@ -242,7 +288,7 @@ public static void InvokeMethod(this object? obj, string methodName, Type[] argT
242288
/// <param name="value">The field value to set on the object.</param>
243289
public static void SetFieldValue<T, TS>(this TS? obj, string fieldName, T? value) where TS : class
244290
{
245-
try { obj?.GetType().InvokeMember(fieldName, BindingFlags.SetField | bindingFlags, null, obj, new object?[] { value }, null); }
291+
try { obj?.GetType().InvokeMember(fieldName, BindingFlags.SetField | bindingFlags, null, obj, [value], null); }
246292
catch { }
247293
}
248294

@@ -266,9 +312,18 @@ public static void SetFieldValue<T, TS>(this ref TS obj, string fieldName, T val
266312
/// <param name="value">The property value to set on the object.</param>
267313
public static void SetPropertyValue<T>(this object obj, string propName, T value)
268314
{
269-
try { obj?.GetType().InvokeMember(propName, BindingFlags.SetProperty | bindingFlags, null, obj, new object?[] { value }, null); }
315+
try { obj?.GetType().InvokeMember(propName, BindingFlags.SetProperty | bindingFlags, null, obj, [value], null); }
270316
catch { }
271317
}
318+
319+
private static bool IsBlittableStruct(this Type? type) => type is not null && type.IsValueType && !type.IsPrimitive && type.IsLayoutSequential && type.GetFields(bindingFlags).All(IsBlittableField);
320+
321+
[StructLayout(LayoutKind.Sequential)]
322+
private struct AlignOfHelper<T> where T : unmanaged
323+
{
324+
public byte dummy;
325+
public T data;
326+
}
272327
}
273328
}
274329

@@ -299,29 +354,6 @@ public static T CreateOrDefault<T>() where T : struct
299354
return default;
300355
}
301356

302-
/// <summary>Gets all loaded types in the <see cref="AppDomain"/>.</summary>
303-
/// <param name="appDomain">The application domain.</param>
304-
/// <returns>All loaded types.</returns>
305-
public static IEnumerable<Type> GetAllTypes(this AppDomain appDomain) => appDomain.GetAssemblies().SelectMany(a => a.GetTypes());
306-
307-
/// <summary>Returns an array of custom attributes applied to this member and identified by <typeparamref name="TAttr"/>.</summary>
308-
/// <typeparam name="TAttr">The type of attribute to search for. Only attributes that are assignable to this type are returned.</typeparam>
309-
/// <param name="element">An object derived from the MemberInfo class that describes a constructor, event, field, method, or property member of a class.</param>
310-
/// <param name="inherit"><c>true</c> to search this member's inheritance chain to find the attributes; otherwise, <c>false</c>. This parameter is ignored for properties and events.</param>
311-
/// <param name="predicate">An optional predicate to refine the results.</param>
312-
/// <returns></returns>
313-
public static IEnumerable<TAttr> GetCustomAttributes<TAttr>(this MemberInfo element, bool inherit = false, Func<TAttr, bool>? predicate = null) where TAttr : Attribute =>
314-
element.GetCustomAttributes(typeof(TAttr), inherit).Cast<TAttr>().Where(predicate ?? (a => true));
315-
316-
/// <summary>Returns an array of custom attributes applied to this member and identified by <typeparamref name="TAttr"/>.</summary>
317-
/// <typeparam name="TAttr">The type of attribute to search for. Only attributes that are assignable to this type are returned.</typeparam>
318-
/// <param name="type">The type of the <see cref="Type"/> to examine.</param>
319-
/// <param name="inherit"><c>true</c> to search this member's inheritance chain to find the attributes; otherwise, <c>false</c>. This parameter is ignored for properties and events.</param>
320-
/// <param name="predicate">An optional predicate to refine the results.</param>
321-
/// <returns></returns>
322-
public static IEnumerable<TAttr> GetCustomAttributes<TAttr>(this Type type, bool inherit = false, Func<TAttr, bool>? predicate = null) where TAttr : Attribute =>
323-
type.GetCustomAttributes(typeof(TAttr), inherit).Cast<TAttr>().Where(predicate ?? (a => true));
324-
325357
/// <summary>Finds the type of the element of a type. Returns null if this type does not enumerate.</summary>
326358
/// <param name="type">The type to check.</param>
327359
/// <returns>The element type, if found; otherwise, <see langword="null"/>.</returns>
@@ -349,6 +381,29 @@ public static IEnumerable<TAttr> GetCustomAttributes<TAttr>(this Type type, bool
349381
bool ImplIEnumT(Type t) => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>);
350382
}
351383

384+
/// <summary>Gets all loaded types in the <see cref="AppDomain"/>.</summary>
385+
/// <param name="appDomain">The application domain.</param>
386+
/// <returns>All loaded types.</returns>
387+
public static IEnumerable<Type> GetAllTypes(this AppDomain appDomain) => appDomain.GetAssemblies().SelectMany(a => a.GetTypes());
388+
389+
/// <summary>Returns an array of custom attributes applied to this member and identified by <typeparamref name="TAttr"/>.</summary>
390+
/// <typeparam name="TAttr">The type of attribute to search for. Only attributes that are assignable to this type are returned.</typeparam>
391+
/// <param name="element">An object derived from the MemberInfo class that describes a constructor, event, field, method, or property member of a class.</param>
392+
/// <param name="inherit"><c>true</c> to search this member's inheritance chain to find the attributes; otherwise, <c>false</c>. This parameter is ignored for properties and events.</param>
393+
/// <param name="predicate">An optional predicate to refine the results.</param>
394+
/// <returns></returns>
395+
public static IEnumerable<TAttr> GetCustomAttributes<TAttr>(this MemberInfo element, bool inherit = false, Func<TAttr, bool>? predicate = null) where TAttr : Attribute =>
396+
element.GetCustomAttributes(typeof(TAttr), inherit).Cast<TAttr>().Where(predicate ?? (a => true));
397+
398+
/// <summary>Returns an array of custom attributes applied to this member and identified by <typeparamref name="TAttr"/>.</summary>
399+
/// <typeparam name="TAttr">The type of attribute to search for. Only attributes that are assignable to this type are returned.</typeparam>
400+
/// <param name="type">The type of the <see cref="Type"/> to examine.</param>
401+
/// <param name="inherit"><c>true</c> to search this member's inheritance chain to find the attributes; otherwise, <c>false</c>. This parameter is ignored for properties and events.</param>
402+
/// <param name="predicate">An optional predicate to refine the results.</param>
403+
/// <returns></returns>
404+
public static IEnumerable<TAttr> GetCustomAttributes<TAttr>(this Type type, bool inherit = false, Func<TAttr, bool>? predicate = null) where TAttr : Attribute =>
405+
type.GetCustomAttributes(typeof(TAttr), inherit).Cast<TAttr>().Where(predicate ?? (a => true));
406+
352407
/// <summary>Gets the fields of a structure with sequential layout in the order in which they appear in memory.</summary>
353408
/// <param name="type">The type of the structure.</param>
354409
/// <param name="bindingFlags">The binding flags.</param>
@@ -391,19 +446,6 @@ public static IEnumerable<FieldInfo> GetOrderedFields(this Type type, BindingFla
391446
return o.InvokeMethod<T>(methodName, args);
392447
}
393448

394-
/// <summary>Invokes a named static method of a type with parameters.</summary>
395-
/// <typeparam name="T">The expected type of the method's return value.</typeparam>
396-
/// <param name="type">The type containing the static method.</param>
397-
/// <param name="methodName">Name of the method.</param>
398-
/// <param name="args">The arguments to provide to the method invocation.</param>
399-
/// <returns>The value returned from the method.</returns>
400-
public static T? InvokeStaticMethod<T>(this Type type, string methodName, params object?[]? args)
401-
{
402-
var argTypes = args == null || args.Length == 0 ? Type.EmptyTypes : Array.ConvertAll(args, o => o?.GetType() ?? typeof(object));
403-
var mi = type.GetMethod(methodName, staticBindingFlags, null, argTypes, null) ?? throw new ArgumentException(@"Method not found", nameof(methodName));
404-
return (T?)mi.Invoke(null, args);
405-
}
406-
407449
#if !NETSTANDARD2_0
408450
/// <summary>Invokes a method from a derived base class.</summary>
409451
/// <param name="methodInfo">The <see cref="MethodInfo"/> instance from the derived class for the method to invoke.</param>
@@ -421,7 +463,7 @@ public static IEnumerable<FieldInfo> GetOrderedFields(this Type type, BindingFla
421463
returnType = methodInfo.ReturnType;
422464

423465
var type = targetObject.GetType();
424-
var dynamicMethod = new DynamicMethod("", returnType, new[] { type, typeof(object) }, type);
466+
var dynamicMethod = new DynamicMethod("", returnType, [type, typeof(object)], type);
425467
var iLGenerator = dynamicMethod.GetILGenerator();
426468
iLGenerator.Emit(OpCodes.Ldarg_0); // this
427469

@@ -442,10 +484,23 @@ public static IEnumerable<FieldInfo> GetOrderedFields(this Type type, BindingFla
442484
iLGenerator.Emit(OpCodes.Call, methodInfo);
443485
iLGenerator.Emit(OpCodes.Ret);
444486

445-
return dynamicMethod.Invoke(null, new[] { targetObject, arguments });
487+
return dynamicMethod.Invoke(null, [targetObject, arguments]);
446488
}
447489
#endif
448490

491+
/// <summary>Invokes a named static method of a type with parameters.</summary>
492+
/// <typeparam name="T">The expected type of the method's return value.</typeparam>
493+
/// <param name="type">The type containing the static method.</param>
494+
/// <param name="methodName">Name of the method.</param>
495+
/// <param name="args">The arguments to provide to the method invocation.</param>
496+
/// <returns>The value returned from the method.</returns>
497+
public static T? InvokeStaticMethod<T>(this Type type, string methodName, params object?[]? args)
498+
{
499+
var argTypes = args == null || args.Length == 0 ? Type.EmptyTypes : Array.ConvertAll(args, o => o?.GetType() ?? typeof(object));
500+
var mi = type.GetMethod(methodName, staticBindingFlags, null, argTypes, null) ?? throw new ArgumentException(@"Method not found", nameof(methodName));
501+
return (T?)mi.Invoke(null, args);
502+
}
503+
449504
/// <summary>Determines whether the specified method is compatible with a delegate.</summary>
450505
/// <typeparam name="TDel">The type of the delegate.</typeparam>
451506
/// <param name="method">The method information.</param>

0 commit comments

Comments
 (0)