Skip to content

Instantly share code, notes, and snippets.

@vtml
Last active February 5, 2021 11:07
Show Gist options
  • Save vtml/f42fe6ec033597a8b0fb3b5910a70df4 to your computer and use it in GitHub Desktop.
Save vtml/f42fe6ec033597a8b0fb3b5910a70df4 to your computer and use it in GitHub Desktop.
Sitecore Super Power Friendly Dependency Injection
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
namespace Isobar.Foundation.SitecoreUtilities.Extensions
{
public interface IReflectionExtensions
{
Assembly[] GetAssemblies(IEnumerable<string> assemblyFilters);
IEnumerable<Type> GetTypesImplementing(Type implementsType, IEnumerable<Assembly> assemblies, params string[] classFilter);
IEnumerable<Type> GetTypesImplementing(Type implementsType, params Assembly[] assemblies);
/// <summary>
/// Extract the default implemented interface. Returns the interface implemented if there's only one, otherwise it will return the closest one with the name matched
/// </summary>
/// <param name="concreteClass"></param>
/// <returns></returns>
Type GetDefaultImplementedInterface(Type concreteClass);
IEnumerable<Type> GetExportedTypes(Assembly assembly);
/// <summary>
/// Checks if a string matches a wildcard argument (using regex)
/// </summary>
bool IsWildcardMatch(string input, string wildcard);
/// <summary>
/// Filter out Interface, Abstracts and Generic Types
/// https://stackoverflow.com/questions/80247/implementations-of-interface-through-reflection
/// </summary>
/// <param name="testType"></param>
/// <returns></returns>
bool IsRealClass(Type testType);
/// <summary>
/// Uses reflection to get the field value from an object.
/// https://stackoverflow.com/questions/3303126/how-to-get-the-value-of-private-field-in-c
/// </summary>
/// <param name="type">The instance type.</param>
/// <param name="instance">The instance object.</param>
/// <param name="fieldName">The field's name which is to be fetched.</param>
/// <returns>The field value from the object.</returns>
object GetInstanceField(Type type, object instance, string fieldName);
}
public class ReflectionExtensions : IReflectionExtensions
{
public Assembly[] GetAssemblies(IEnumerable<string> assemblyFilters)
{
var assemblies = new List<Assembly>();
foreach (var assemblyFilter in assemblyFilters)
{
assemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies().Where(assembly => IsWildcardMatch(assembly.GetName().Name, assemblyFilter)).ToArray());
}
return assemblies.ToArray();
}
public IEnumerable<Type> GetTypesImplementing(Type implementsType, IEnumerable<Assembly> assemblies, params string[] classFilter)
{
var types = GetTypesImplementing(implementsType, assemblies.ToArray());
if (classFilter != null && classFilter.Any())
{
types = types.Where(type => classFilter.Any(filter => IsWildcardMatch(type.FullName, filter)));
}
return types;
}
public IEnumerable<Type> GetTypesImplementing(Type implementsType, params Assembly[] assemblies)
{
if (assemblies == null || assemblies.Length == 0)
{
return new Type[0];
}
var targetType = implementsType;
return assemblies
.Where(assembly => !assembly.IsDynamic)
.SelectMany(GetExportedTypes)
.Where(type => !type.IsAbstract && !type.IsGenericTypeDefinition && targetType.IsAssignableFrom(type))
.ToArray();
}
public Type GetDefaultImplementedInterface(Type concreteClass)
{
if (!IsRealClass(concreteClass)) return null;
var concreteClassName = concreteClass.Name;
var mockUpInterfaceName = string.Format("I{0}", concreteClassName);
var defaultInterface = concreteClass.GetInterfaces();
if (defaultInterface.Length.Equals(1)) return defaultInterface.FirstOrDefault();
var closesetMatchedInterface = defaultInterface.Where(i => i.Name.StartsWith(mockUpInterfaceName, StringComparison.OrdinalIgnoreCase)).OrderBy(i => i.Name.Length - mockUpInterfaceName.Length);
var defaultInterfaceList = closesetMatchedInterface as IList<Type> ?? closesetMatchedInterface.ToList();
return defaultInterfaceList.Any() ? defaultInterfaceList.FirstOrDefault() : null;
}
public IEnumerable<Type> GetExportedTypes(Assembly assembly)
{
try
{
return assembly.GetExportedTypes();
}
catch (NotSupportedException)
{
// A type load exception would typically happen on an Anonymously Hosted DynamicMethods
// Assembly and it would be safe to skip this exception.
return Type.EmptyTypes;
}
catch (FileLoadException)
{
// The assembly points to a not found assembly - ignore and continue
return Type.EmptyTypes;
}
catch (ReflectionTypeLoadException ex)
{
// Return the types that could be loaded. Types can contain null values.
return ex.Types.Where(type => type != null);
}
catch (Exception ex)
{
// Throw a more descriptive message containing the name of the assembly.
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to load types from assembly {0}. {1}", assembly.FullName, ex.Message), ex);
}
}
public bool IsWildcardMatch(string input, string wildcard)
{
return input == wildcard || Regex.IsMatch(input, "^" + Regex.Escape(wildcard).Replace("\\*", ".*").Replace("\\?", ".") + "$", RegexOptions.IgnoreCase);
}
public bool IsRealClass(Type testType)
{
return testType.IsAbstract == false && testType.IsGenericTypeDefinition == false && testType.IsInterface == false;
}
public object GetInstanceField(Type type, object instance, string fieldName)
{
const BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
var field = type.GetField(fieldName, bindFlags);
return field != null ? field.GetValue(instance) : null;
}
}
}
@SoulOfUniverse
Copy link

Line 131 IsWIldCardMatch you have a bug out there, about to submit a correct solution.

@vtml
Copy link
Author

vtml commented Feb 5, 2021

Line 131 IsWIldCardMatch you have a bug out there, about to submit a correct solution.

Thank you, that'll be awesome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment