Lors de l'utilisation de la réflexion que vous devez vous poser quelques questions, d'abord, parce que vous pouvez vous retrouver dans un au-dessus de la solution complexe qui est difficile à maintenir:
- Est-il un moyen de résoudre le problème en utilisant genericity ou de classe/l'héritage de l'interface?
- Puis-je résoudre le problème à l'aide d'
dynamic
invocations (seule .NET 4.0 et ci-dessus)?
- La performance est importante, c'est à dire mon reflète la méthode ou l'instanciation d'un appel d'être appelée une fois, deux fois ou un million de fois?
- Puis-je combiner des technologies pour arriver à une smart mais efficace/compréhensible solution?
- Je suis ok avec perte de temps de compilation de sécurité de type?
Genericity / dynamique
À partir de votre description, je suppose que vous ne connaissez pas le type au moment de la compilation, vous ne connaissez qu'ils partagent l'interface ICalculation
. Si cela est correct, alors le nombre (1) et (2) ci-dessus sont susceptibles de ne pas possible dans votre scénario.
Performance
C'est une question importante à se poser. La surcharge de l'aide de la réflexion peut entraver une plus de 400 fois la pénalité: qui ralentit même une quantité modérée d'appels.
La résolution est relativement simple: au lieu d'utiliser Activator.CreateInstance
, utilisez une méthode de fabrique (vous l'avez déjà), de rechercher l' MethodInfo
créer un délégué, le cache et l'utilisation de la déléguer à partir de là. Cela donne seulement une pénalité sur le premier appel, à la suite d'invocations ont une performance quasi-native.
Combiner des technologies
Tout est possible ici, mais j'avais vraiment besoin d'en savoir plus sur votre situation afin de l'aider dans ce sens. Souvent, j'arrive à la fin combinant dynamic
avec les génériques, avec mise en cache de la réflexion. Lors de l'utilisation de se cacher de l'information (comme il est normal, en programmation orientée objet), vous pouvez vous retrouver avec un rapide, stable et toujours le bien-solution évolutive.
Perdre de la compilation de sécurité de type
De ces cinq questions, c'est peut-être le plus important à s'inquiéter. Il est très important de créer vos propres exceptions que de donner de l'information claire sur la réflexion des erreurs. Cela signifie que chaque appel d'une méthode, constructeur ou bien sur la base d'une chaîne d'entrée ou autrement décoché l'information doit être enveloppé dans un try/catch. Attraper des exceptions spécifiques (comme toujours, je veux dire: ne jamais attraper Exception
lui-même).
Focus sur TargetException
(méthode n'existe pas), TargetInvocationException
(méthode existe, mais a un exc. lorsqu'il est invoqué), TargetParameterCountException
, MethodAccessException
(pas le droit de privilèges, il se passe beaucoup de choses dans ASP.NET), InvalidOperationException
(c'est le cas avec les types génériques). Vous n'avez pas toujours besoin d'essayer toutes les attraper, il dépend de la contribution qui est attendue et prévue des objets cibles.
Pour résumer
Débarrassez-vous de vos Activator.CreateInstance
et l'utilisation MethodInfo pour trouver l'usine de méthode de création et d'utilisation Delegate.CreateDelegate
de créer et de mettre en cache le délégué. Il suffit de stocker dans un statique Dictionary
où la clé est égale à la classe string dans votre exemple de code. Ci-dessous un rapide mais pas si sale façon de le faire en toute sécurité et sans perdre trop de type de sécurité.
Exemple de code
public class TestDynamicFactory
{
// static storage
private static Dictionary<string, Func<ICalculate>> InstanceCreateCache = new Dictionary<string, Func<ICalculate>>();
// how to invoke it
static int Main()
{
// invoke it, this is lightning fast and the first-time cache will be arranged
// also, no need to give the full method anymore, just the classname, as we
// use an interface for the rest. Almost full type safety!
ICalculate instanceOfCalculator = this.CreateCachableICalculate("RandomNumber");
int result = instanceOfCalculator.ExecuteCalculation();
}
// searches for the class, initiates it (calls factory method) and returns the instance
// TODO: add a lot of error handling!
ICalculate CreateCachableICalculate(string className)
{
if(!InstanceCreateCache.ContainsKey(className))
{
// get the type (several ways exist, this is an eays one)
Type type = TypeDelegator.GetType("TestDynamicFactory." + className);
// NOTE: this can be tempting, but do NOT use the following, because you cannot
// create a delegate from a ctor and will loose many performance benefits
//ConstructorInfo constructorInfo = type.GetConstructor(Type.EmptyTypes);
// works with public instance/static methods
MethodInfo mi = type.GetMethod("Create");
// the "magic", turn it into a delegate
var createInstanceDelegate = (Func<ICalculate>) Delegate.CreateDelegate(typeof (Func<ICalculate>), mi);
// store for future reference
InstanceCreateCache.Add(className, createInstanceDelegate);
}
return InstanceCreateCache[className].Invoke();
}
}
// example of your ICalculate interface
public interface ICalculate
{
void Initialize();
int ExecuteCalculation();
}
// example of an ICalculate class
public class RandomNumber : ICalculate
{
private static Random _random;
public static RandomNumber Create()
{
var random = new RandomNumber();
random.Initialize();
return random;
}
public void Initialize()
{
_random = new Random(DateTime.Now.Millisecond);
}
public int ExecuteCalculation()
{
return _random.Next();
}
}