Skip to content

Instantly share code, notes, and snippets.

@xoofx
Last active June 29, 2021 10:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xoofx/710aaf86e0e8c81649d1261b1ef9590e to your computer and use it in GitHub Desktop.
Save xoofx/710aaf86e0e8c81649d1261b1ef9590e to your computer and use it in GitHub Desktop.
Gets the list of the generic type instances used in an assembly
public static class AssemblyHelper
{
/// <summary>
/// Gets the list of concrete generic type instances used in an assembly.
/// See remarks
/// </summary>
/// <param name="assembly">The assembly</param>
/// <returns>The list of generic type instances</returns>
/// <remarks>
/// Note that this method is fetching only direct type instances (through type, method argument or fields)
/// </remarks>
public static List<Type> GetGenericTypeInstances(System.Reflection.Assembly assembly)
{
var types = new List<Type>();
var visited = new HashSet<Type>();
CollectGenericTypeInstances(assembly, types, visited);
return types;
}
private static void CollectGenericTypeInstances(System.Reflection.Assembly assembly, List<Type> types, HashSet<Type> visited)
{
// From: https://gist.github.com/xoofx/710aaf86e0e8c81649d1261b1ef9590e
if (assembly == null) throw new ArgumentNullException(nameof(assembly));
const int mdMaxCount = 1 << 24;
foreach (var module in assembly.Modules)
{
for (int i = 1; i < mdMaxCount; i++)
{
try
{
// Token base id for TypeSpec
const int mdTypeSpec = 0x1B000000;
var type = module.ResolveType(mdTypeSpec | i);
if (type.IsConstructedGenericType && !type.ContainsGenericParameters)
{
CollectGenericTypeInstances(type, types, visited);
}
}
catch (ArgumentOutOfRangeException)
{
break;
}
catch (ArgumentException)
{
// Can happen on ResolveType on certain generic types, so we continue
}
}
for (int i = 1; i < mdMaxCount; i++)
{
try
{
// Token base id for MethodSpec
const int mdMethodSpec = 0x2B000000;
var method = module.ResolveMethod(mdMethodSpec | i);
var genericArgs = method.GetGenericArguments();
foreach (var genArgType in genericArgs)
{
if (genArgType.IsConstructedGenericType && !genArgType.ContainsGenericParameters)
{
CollectGenericTypeInstances(genArgType, types, visited);
}
}
}
catch (ArgumentOutOfRangeException)
{
break;
}
catch (ArgumentException)
{
// Can happen on ResolveType on certain generic types, so we continue
}
}
for (int i = 1; i < mdMaxCount; i++)
{
try
{
// Token base id for Field
const int mdField = 0x04000000;
var field = module.ResolveField(mdField | i);
CollectGenericTypeInstances(field.FieldType, types, visited);
}
catch (ArgumentOutOfRangeException)
{
break;
}
catch (ArgumentException)
{
// Can happen on ResolveType on certain generic types, so we continue
}
}
// Scan for types used in constructor arguments to assembly-level attributes
foreach (var customAttribute in assembly.CustomAttributes)
{
foreach (var argument in customAttribute.ConstructorArguments)
{
if (argument.ArgumentType == typeof(Type))
{
CollectGenericTypeInstances((Type)argument.Value, types, visited, typeFilter);
}
}
}
}
}
private static void CollectGenericTypeInstances(Type type, List<Type> types, HashSet<Type> visited)
{
if (type.IsPrimitive) return;
if (!visited.Add(type)) return;
// Add only concrete types
if (type.IsConstructedGenericType && !type.ContainsGenericParameters)
{
types.Add(type);
}
// Collect recursively generic type arguments
var genericTypeArguments = type.GenericTypeArguments;
foreach (var genericTypeArgument in genericTypeArguments)
{
if (!genericTypeArgument.IsPrimitive)
{
CollectGenericTypeInstances(genericTypeArgument, types, visited);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment