Pour une classe donnée, je voudrais avoir une fonctionnalité de traçage, c'est-à-dire que je voudrais enregistrer chaque appel de méthode (signature de méthode et valeurs réelles des paramètres) et chaque sortie de méthode (juste la signature de méthode).
Comment puis-je accomplir cela en supposant que:
- Je ne souhaite pas utiliser de bibliothèques AOP tierces pour C #,
- Je ne veux pas ajouter de code en double à toutes les méthodes que je souhaite tracer,
- Je ne veux pas changer l'API publique de la classe - les utilisateurs de la classe devraient pouvoir appeler toutes les méthodes exactement de la même manière.
Pour rendre la question plus concrète, supposons qu'il existe 3 classes:
public class Caller
{
public static void Call()
{
Traced traced = new Traced();
traced.Method1();
traced.Method2();
}
}
public class Traced
{
public void Method1(String name, Int32 value) { }
public void Method2(Object object) { }
}
public class Logger
{
public static void LogStart(MethodInfo method, Object[] parameterValues);
public static void LogEnd(MethodInfo method);
}
Comment appeler Logger.LogStart et Logger.LogEnd pour chaque appel à Method1 et Method2 sans modifier la méthode Caller.Call et sans ajouter explicitement les appels à Traced.Method1 et Traced.Method2 ?
Edit: Quelle serait la solution si je suis autorisé à modifier légèrement la méthode Call?
c#
reflection
aop
Compagnon
la source
la source
Réponses:
C # n'est pas un langage orienté AOP. Il a quelques fonctionnalités AOP et vous pouvez en émuler d'autres, mais créer AOP avec C # est douloureux.
J'ai recherché des moyens de faire exactement ce que vous vouliez faire et je n'ai trouvé aucun moyen facile de le faire.
Si je comprends bien, voici ce que vous voulez faire:
et pour ce faire, vous avez deux options principales
Héritez votre classe de MarshalByRefObject ou ContextBoundObject et définissez un attribut qui hérite de IMessageSink. Cet article a un bon exemple. Vous devez néanmoins considérer qu'en utilisant un MarshalByRefObject, les performances seront terribles, et je le pense, je parle d'une performance 10x perdue, alors réfléchissez bien avant d'essayer cela.
L'autre option est d'injecter du code directement. Au moment de l'exécution, cela signifie que vous devrez utiliser la réflexion pour «lire» chaque classe, obtenir ses attributs et injecter l'appel approprié (et d'ailleurs je pense que vous ne pouvez pas utiliser la méthode Reflection.Emit comme je pense que Reflection.Emit ne le ferait pas) ne vous permet pas d'insérer un nouveau code dans une méthode déjà existante). Au moment de la conception, cela signifiera la création d'une extension du compilateur CLR dont je n'ai honnêtement aucune idée de la façon dont cela est fait.
La dernière option utilise un framework IoC . Ce n'est peut-être pas la solution parfaite car la plupart des frameworks IoC fonctionnent en définissant des points d'entrée qui permettent aux méthodes d'être accrochées, mais, en fonction de ce que vous souhaitez obtenir, cela pourrait être une approximation juste.
la source
Reflection.Emit
. C'est l' approche choisie par Spring.NET . Cependant, cela nécessiterait des méthodes virtuellesTraced
et ne convient pas vraiment à une utilisation sans une sorte de conteneur IOC, je comprends donc pourquoi cette option ne figure pas dans votre liste.Le moyen le plus simple d'y parvenir est probablement d'utiliser PostSharp . Il injecte du code dans vos méthodes en fonction des attributs que vous lui appliquez. Cela vous permet de faire exactement ce que vous voulez.
Une autre option consiste à utiliser l' API de profilage pour injecter du code dans la méthode, mais c'est vraiment hardcore.
la source
Si vous écrivez une classe - appelez-la Tracing - qui implémente l'interface IDisposable, vous pouvez envelopper tous les corps de méthode dans un
Dans la classe Tracing, vous pouvez gérer la logique des traces dans la méthode constructeur / Dispose, respectivement, dans la classe Tracing pour garder une trace de l'entrée et de la sortie des méthodes. Tel que:
la source
Vous pouvez y parvenir avec la fonction d' interception d'un conteneur DI tel que Castle Windsor . En effet, il est possible de configurer le conteneur de manière à ce que toutes les classes ayant une méthode décorée par un attribut spécifique soient interceptées.
Concernant le point n ° 3, OP a demandé une solution sans cadre AOP. J'ai supposé dans la réponse suivante que ce qui devrait être évité était Aspect, JointPoint, PointCut, etc. Selon la documentation d'interception de CastleWindsor , aucun de ceux-ci n'est requis pour accomplir ce qui est demandé.
Configurez l'enregistrement générique d'un intercepteur, basé sur la présence d'un attribut:
Ajouter le IContributeComponentModelConstruction créé au conteneur
Et tu peux faire ce que tu veux dans l'intercepteur lui-même
Ajoutez l'attribut de journalisation à votre méthode pour journaliser
Notez qu'une certaine manipulation de l'attribut sera nécessaire si seule une méthode d'une classe doit être interceptée. Par défaut, toutes les méthodes publiques seront interceptées.
la source
Si vous voulez suivre vos méthodes sans limitation (pas d'adaptation de code, pas de Framework AOP, pas de code en double), laissez-moi vous dire, vous avez besoin d'un peu de magie ...
Sérieusement, je l'ai résolu pour implémenter un Framework AOP fonctionnant au moment de l'exécution.
Vous pouvez trouver ici: NConcern .NET AOP Framework
J'ai décidé de créer ce Framework AOP pour répondre à ce type de besoins. c'est une bibliothèque simple très légère. Vous pouvez voir un exemple de logger sur la page d'accueil.
Si vous ne souhaitez pas utiliser un assembly tiers, vous pouvez parcourir le code source (open source) et copier les deux fichiers Aspect.Directory.cs et Aspect.Directory.Entry.cs pour les adapter selon vos souhaits. Ces classes permettent de remplacer vos méthodes lors de l'exécution. Je vous demanderais simplement de respecter la licence.
J'espère que vous trouverez ce dont vous avez besoin ou pour vous convaincre d'utiliser enfin un Framework AOP.
la source
Jetez un oeil à ceci - Trucs assez lourds .. http://msdn.microsoft.com/en-us/magazine/cc164165.aspx
Essential .net - don box avait un chapitre sur ce dont vous avez besoin appelé Interception. J'en ai gratté une partie ici (Désolé pour les couleurs de police - j'avais un thème sombre à l'époque ...) http://madcoderspeak.blogspot.com/2005/09/essential-interception-using-contexts.html
la source
J'ai trouvé un moyen différent qui peut être plus facile ...
Déclarer une méthode InvokeMethod
Je définis ensuite mes méthodes comme ça
Maintenant, je peux avoir la vérification au moment de l'exécution sans l'injection de dépendance ...
Pas de pièges sur le site :)
J'espère que vous conviendrez qu'il s'agit d'un poids moindre qu'un Framework AOP ou dérivant de MarshalByRefObject ou utilisant des classes à distance ou proxy.
la source
Vous devez d'abord modifier votre classe pour implémenter une interface (plutôt que d'implémenter MarshalByRefObject).
Ensuite, vous avez besoin d'un objet wrapper générique basé sur RealProxy pour décorer n'importe quelle interface afin de permettre l'interception de tout appel à l'objet décoré.
Nous sommes maintenant prêts à intercepter les appels à la méthode 1 et à la méthode 2 de ITraced
la source
Vous pouvez utiliser le framework open source CInject sur CodePlex. Vous pouvez écrire un code minimal pour créer un injecteur et le faire intercepter rapidement tout code avec CInject. De plus, comme il s'agit d'Open Source, vous pouvez également l'étendre.
Vous pouvez également suivre les étapes mentionnées dans cet article sur les appels de méthode d'interception à l'aide d'IL et créer votre propre intercepteur à l'aide des classes Reflection.Emit en C #.
la source
Je ne connais pas de solution mais mon approche serait la suivante.
Décorez la classe (ou ses méthodes) avec un attribut personnalisé. A un autre endroit du programme, laissez une fonction d'initialisation refléter tous les types, lisez les méthodes décorées avec les attributs et injectez du code IL dans la méthode. Il pourrait en fait être plus pratique de remplacer la méthode par un stub qui appelle
LogStart
, la méthode réelle, puisLogEnd
. De plus, je ne sais pas si vous pouvez modifier les méthodes en utilisant la réflexion, il pourrait donc être plus pratique de remplacer le type entier.la source
Vous pouvez potentiellement utiliser le modèle GOF Decorator et «décorer» toutes les classes qui nécessitent un traçage.
Ce n'est probablement vraiment pratique qu'avec un conteneur IOC (mais comme pointeur plus tôt, vous voudrez peut-être envisager une interception de méthode si vous allez suivre le chemin IOC).
la source
vous devez bogue Ayende pour une réponse sur la façon dont il l'a fait: http://ayende.com/Blog/archive/2009/11/19/can-you-hack-this-out.aspx
la source
AOP est un must pour l'implémentation de code propre, mais si vous souhaitez entourer un bloc en C #, les méthodes génériques ont une utilisation relativement plus facile. (avec un sens intelli et un code fortement typé) Certes, cela ne peut PAS être une alternative à AOP.
Bien que PostSHarp ait peu de problèmes de buggy (je ne me sens pas confiant pour l'utilisation en production), c'est une bonne chose.
Classe wrapper générique,
l'utilisation pourrait être comme ça (avec un sens intelli bien sûr)
la source
la source
Le mieux que vous puissiez faire avant la sortie de C # 6 avec 'nameof' est d'utiliser des expressions lentes StackTrace et linq.
Par exemple pour une telle méthode
Une telle ligne peut être produite dans votre fichier journal
Voici la mise en œuvre:
la source