using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;
using Pfz.Extensions.MonitorLockExtensions;
namespace Pfz.Extensions.TypeExtensions
{
/// <summary>
/// Adds some methods to the Type class so you can discover the
/// sub-types easily.
/// </summary>
public static class PfzTypeExtensions
{
#region GetDirectSubClasses
private static Dictionary<KeyValuePair<Type, Assembly>, ReadOnlyCollection<Type>> fSubClasses = new Dictionary<KeyValuePair<Type, Assembly>, ReadOnlyCollection<Type>>();
/// <summary>
/// Gets the sub-classes of the specific type, in the specific assembly.
/// </summary>
public static ReadOnlyCollection<Type> GetDirectSubClasses(this Type type, Assembly inAssembly)
{
ReadOnlyCollection<Type> result = null;
KeyValuePair<Type, Assembly> pair = new KeyValuePair<Type,Assembly>(type, inAssembly);
fSubClasses.LockWithTimeout
(
delegate
{
if (!fSubClasses.TryGetValue(pair, out result))
{
List<Type> list = new List<Type>();
foreach(Type possibleSubType in inAssembly.GetTypes())
if (possibleSubType.BaseType == type)
list.Add(possibleSubType);
result = new ReadOnlyCollection<Type>(list.ToArray());
fSubClasses.Add(pair, result);
}
}
);
return result;
}
#endregion
#region GetSubClassesRecursive
private static Dictionary<KeyValuePair<Type, Assembly>, ReadOnlyCollection<Type>> fSubClassesRecursive = new Dictionary<KeyValuePair<Type, Assembly>, ReadOnlyCollection<Type>>();
/// <summary>
/// Gets the sub-classes of the specific type, in the specific assembly.
/// </summary>
public static ReadOnlyCollection<Type> GetSubClassesRecursive(this Type type, Assembly inAssembly)
{
ReadOnlyCollection<Type> result = null;
KeyValuePair<Type, Assembly> pair = new KeyValuePair<Type,Assembly>(type, inAssembly);
fSubClassesRecursive.LockWithTimeout
(
delegate
{
if (!fSubClassesRecursive.TryGetValue(pair, out result))
{
List<Type> list = new List<Type>();
foreach(Type possibleSubType in inAssembly.GetTypes())
if (possibleSubType != type && type.IsAssignableFrom(possibleSubType))
list.Add(possibleSubType);
result = new ReadOnlyCollection<Type>(list.ToArray());
fSubClassesRecursive.Add(pair, result);
}
}
);
return result;
}
#endregion
#region GetReferencedAssemblies
/// <summary>
/// Gets all assemblies that must be referenced if you want to
/// use everything from the given type. Useful for CodeDOM.
/// </summary>
/// <param name="type">The type to get all referenced assemblies.</param>
/// <returns>A hashset with all referenced assemblies.</returns>
public static HashSet<Assembly> GetReferencedAssemblies(this Type type)
{
HashSet<Type> referencedTypes = new HashSet<Type>();
HashSet<Assembly> referencedAssemblies = new HashSet<Assembly>();
AddReferencedAssemblies(type, referencedAssemblies, referencedTypes);
return referencedAssemblies;
}
/// <summary>
/// Gets all assemblies that must be referenced if you want to
/// use everything from the given types. Useful for CodeDOM.
/// </summary>
/// <param name="types">A collection of all needed types.</param>
/// <returns>A HashSet of all referenced assemblies.</returns>
public static HashSet<Assembly> GetReferencedAssemblies(this IEnumerable<Type> types)
{
HashSet<Type> processedTypes = new HashSet<Type>();
HashSet<Assembly> processedAssemblies = new HashSet<Assembly>();
foreach(Type type in types)
AddReferencedAssemblies(type, processedAssemblies, processedTypes);
return processedAssemblies;
}
#endregion
#region AddReferencedAssemblies
/// <summary>
/// Adds all the referenced types/assemblies into the given hashsets.
/// This is almost an internal implementation, but if you need to add
/// many references but do not have a collection previouly created, you
/// must use this method for performance reasons.
/// </summary>
/// <param name="type">The type to get all the referencies.</param>
/// <param name="alreadyAddedAssemblies">A hashset, that is used to add the new referenced assemblies.</param>
/// <param name="alreadyAddedTypes">A hashset, that is used to add the new referenced types and to avoid reprocessing them.</param>
public static void AddReferencedAssemblies(this Type type, HashSet<Assembly> alreadyAddedAssemblies, HashSet<Type> alreadyAddedTypes)
{
if (type == null)
return;
// returns if this type was already processed.
if (!alreadyAddedTypes.Add(type))
return;
alreadyAddedAssemblies.Add(type.Assembly);
AddReferencedAssemblies(type.BaseType, alreadyAddedAssemblies, alreadyAddedTypes);
AddReferencedAssemblies(type.DeclaringType, alreadyAddedAssemblies, alreadyAddedTypes);
foreach(Type interfaceType in type.GetInterfaces())
AddReferencedAssemblies(interfaceType, alreadyAddedAssemblies, alreadyAddedTypes);
foreach(FieldInfo field in type.GetFields())
AddReferencedAssemblies(field.FieldType, alreadyAddedAssemblies, alreadyAddedTypes);
foreach(MethodInfo method in type.GetMethods())
{
AddReferencedAssemblies(method.ReturnType, alreadyAddedAssemblies, alreadyAddedTypes);
foreach(ParameterInfo parameter in method.GetParameters())
AddReferencedAssemblies(parameter.ParameterType, alreadyAddedAssemblies, alreadyAddedTypes);
}
foreach(PropertyInfo property in type.GetProperties())
{
AddReferencedAssemblies(property.PropertyType, alreadyAddedAssemblies, alreadyAddedTypes);
foreach(ParameterInfo parameter in property.GetIndexParameters())
AddReferencedAssemblies(parameter.ParameterType, alreadyAddedAssemblies, alreadyAddedTypes);
}
foreach(EventInfo eventInfo in type.GetEvents())
AddReferencedAssemblies(eventInfo.EventHandlerType, alreadyAddedAssemblies, alreadyAddedTypes);
}
#endregion
}
}