.NET generic types - finding most specific type
Are there any good algorithms for determining the "best" type to instantiate in order to fulfill a request?
For instance say I have the following classes:
public interface ISometype<T> {}
public class SomeTypeImpl<T>:ISometype<T> {}
public class SomeSpecificTypeImpl<T>:ISometype<T> where T: ISpecific开发者_运维技巧Specifier {}
public interface ISpecificSpecifier { }
Suppose a caller wants the best implementation type for this interface. I could implement this particular method like this:
public Type GetBestImplementationType(Type genericParam) {
try {
return typeof(SomeSpecificTypeImpl<>).MakeGenericType(genericParam);
} catch(ArgumentException) {}
return typeof(SomeTypeImpl<>).MakeGenericType(genericParam);
}
While this implementation will work for this particular case I am more concerned about the generalizations where there may be more than one potential specific implementation and multiple generic parameters:
public Type GetBestImplementationType(Type[] genericParams, Type[] potentialTypes) {
foreach(var t in potentialTypes) {
try {
return t.MakeGenericType(genericParams);
} catch(ArgumentException) {}
}
throw new Exception("unable to find specific implementation type");
}
This should work given the potentialTypes array is provided from most to least specific order. So for answers, either an algorithm implementing this method (or something sufficiently similar) or an algorithm implementing the sort that I could use in this method would do.
[warning: code untested, syntax/logic errors may exist]I think that the only way to do that is to iterate over all classes in all assemblies which could be quite slow.
Here is how asp.net MVC search for all controllers in the project:
private static List<Type> GetAllControllerTypes(IBuildManager buildManager) {
// Go through all assemblies referenced by the application and search for
// controllers and controller factories.
List<Type> controllerTypes = new List<Type>();
ICollection assemblies = buildManager.GetReferencedAssemblies();
foreach (Assembly assembly in assemblies) {
Type[] typesInAsm;
try {
typesInAsm = assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex) {
typesInAsm = ex.Types;
}
controllerTypes.AddRange(typesInAsm.Where(IsControllerType));
}
return controllerTypes;
}
In your case you can rework the code to something similar:
private static List<Type> GetAllSubtypesOf(Type anInterface) {
List<Type> types = new List<Type>();
ICollection assemblies = buildManager.GetReferencedAssemblies();
foreach (Assembly assembly in assemblies) {
Type[] typesInAsm;
try {
typesInAsm = assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex) {
typesInAsm = ex.Types;
}
types.AddRange(typesInAsm.Where(t => anInterface.IsAssignableFrom(t)));
}
return types;
}
Note that because iterating over all assemblies is quite inefficient asp.net MVC do it once and the cache the results.
It seems that:
- There is no better way to determine if a generic type's constraints are met other than to attempt to create the type and catch the exception (it can be done, but seems to take at least as long as the exception method and is far more complex).
- Because of #1, it is very difficult [computationally] to order a set of types from most to least specific. Instead in code my solution was to explicitly tell my container how to order the types.
精彩评论