using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Swan
{
///
/// Provides various extension methods for Reflection and Types.
///
public static class ReflectionExtensions
{
///
/// Gets all types within an assembly in a safe manner.
///
/// The assembly.
///
/// Array of Type objects representing the types specified by an assembly.
///
/// assembly.
public static IEnumerable GetAllTypes(this Assembly assembly)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
return e.Types.Where(t => t != null);
}
}
#region Type Extensions
///
/// The closest programmatic equivalent of default(T).
///
/// The type.
///
/// Default value of this type.
///
/// type.
public static object? GetDefault(this Type type)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
return type.IsValueType ? Activator.CreateInstance(type) : default;
}
///
/// Determines whether this type is compatible with ICollection.
///
/// The type.
///
/// true if the specified source type is collection; otherwise, false.
///
/// sourceType.
public static bool IsCollection(this Type sourceType)
{
if (sourceType == null)
throw new ArgumentNullException(nameof(sourceType));
return sourceType != typeof(string) &&
typeof(IEnumerable).IsAssignableFrom(sourceType);
}
///
/// Gets a method from a type given the method name, binding flags, generic types and parameter types.
///
/// Type of the source.
/// The binding flags.
/// Name of the method.
/// The generic types.
/// The parameter types.
///
/// An object that represents the method with the specified name.
///
///
/// The exception that is thrown when binding to a member results in more than one member matching the
/// binding criteria. This class cannot be inherited.
///
public static MethodInfo GetMethod(
this Type type,
BindingFlags bindingFlags,
string methodName,
Type[] genericTypes,
Type[] parameterTypes)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (methodName == null)
throw new ArgumentNullException(nameof(methodName));
if (genericTypes == null)
throw new ArgumentNullException(nameof(genericTypes));
if (parameterTypes == null)
throw new ArgumentNullException(nameof(parameterTypes));
var methods = type
.GetMethods(bindingFlags)
.Where(mi => string.Equals(methodName, mi.Name, StringComparison.Ordinal))
.Where(mi => mi.ContainsGenericParameters)
.Where(mi => mi.GetGenericArguments().Length == genericTypes.Length)
.Where(mi => mi.GetParameters().Length == parameterTypes.Length)
.Select(mi => mi.MakeGenericMethod(genericTypes))
.Where(mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes))
.ToList();
return methods.Count > 1 ? throw new AmbiguousMatchException() : methods.FirstOrDefault();
}
///
/// Determines whether [is i enumerable request].
///
/// The type.
///
/// true if [is i enumerable request] [the specified type]; otherwise, false.
///
/// type.
public static bool IsIEnumerable(this Type type)
=> type == null
? throw new ArgumentNullException(nameof(type))
: type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
#endregion
///
/// Tries to parse using the basic types.
///
/// The type.
/// The value.
/// The result.
///
/// true if parsing was successful; otherwise, false.
///
/// type
public static bool TryParseBasicType(this Type type, object value, out object? result)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (type != typeof(bool))
return TryParseBasicType(type, value.ToStringInvariant(), out result);
result = value.ToBoolean();
return true;
}
///
/// Tries to parse using the basic types.
///
/// The type.
/// The value.
/// The result.
///
/// true if parsing was successful; otherwise, false.
///
/// type
public static bool TryParseBasicType(this Type type, string value, out object? result)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
result = null;
return Definitions.BasicTypesInfo.Value.ContainsKey(type) && Definitions.BasicTypesInfo.Value[type].TryParse(value, out result);
}
///
/// Tries the type of the set basic value to a property.
///
/// The property information.
/// The value.
/// The object.
///
/// true if parsing was successful; otherwise, false.
///
/// propertyInfo.
public static bool TrySetBasicType(this PropertyInfo propertyInfo, object value, object target)
{
if (propertyInfo == null)
throw new ArgumentNullException(nameof(propertyInfo));
try
{
if (propertyInfo.PropertyType.TryParseBasicType(value, out var propertyValue))
{
propertyInfo.SetValue(target, propertyValue);
return true;
}
}
#pragma warning disable CA1031 // Do not catch general exception types
catch
#pragma warning restore CA1031 // Do not catch general exception types
{
// swallow
}
return false;
}
///
/// Tries the type of the set to an array a basic type.
///
/// The type.
/// The value.
/// The array.
/// The index.
///
/// true if parsing was successful; otherwise, false.
///
/// type
public static bool TrySetArrayBasicType(this Type type, object value, Array target, int index)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (target == null)
return false;
try
{
if (value == null)
{
target.SetValue(null, index);
return true;
}
if (type.TryParseBasicType(value, out var propertyValue))
{
target.SetValue(propertyValue, index);
return true;
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
target.SetValue(null, index);
return true;
}
}
#pragma warning disable CA1031 // Do not catch general exception types
catch
#pragma warning restore CA1031 // Do not catch general exception types
{
// swallow
}
return false;
}
///
/// Tries to set a property array with another array.
///
/// The property.
/// The value.
/// The object.
///
/// true if parsing was successful; otherwise, false.
///
/// propertyInfo.
public static bool TrySetArray(this PropertyInfo propertyInfo, IEnumerable