Pouvez-vous utiliser reflection pour trouver le nom de la méthode en cours d'exécution?
Comme le titre dit: la réflexion peut-elle vous donner le nom de la méthode en cours d'exécution.
Je suis enclin à deviner non, à cause du problème de Heisenberg. Comment appelez-vous une méthode qui vous indiquera la méthode actuelle sans changer ce que la méthode actuelle est? Mais j'espère que quelqu'un pourra me prouver le contraire.
Mise à Jour:
- Partie 2: Cela pourrait-il être utilisé pour regarder à l'intérieur du code pour une propriété?
- Partie 3: Quelle serait la performance être comme?
Résultat Final
J'ai appris à propos de MethodBase.GetCurrentMethod (). J'ai également appris que non seulement je peux créer une trace de pile, je peux créer seulement le cadre exact dont j'ai besoin si je veux.
Pour l'utiliser dans une propriété, il suffit de prendre un .Sous-chaîne (4) pour supprimer le 'set_' ou 'get_'.
13 réponses
Comme de .NET 4.5, vous pouvez également utiliser [CallerMemberName]
Exemple: un setter de propriété (pour répondre à la partie 2):
protected void SetProperty<T>(T value, [CallerMemberName] string property = null)
{
this.propertyValues[property] = value;
OnPropertyChanged(property);
}
public string SomeProperty
{
set { SetProperty(value); }
}
Le compilateur fournira des littéraux de chaîne correspondants aux callsites, donc il n'y a fondamentalement pas de surcharge de performance.
System.Reflection.MethodBase.GetCurrentMethod().Name;
Http://msdn.microsoft.com/en-us/library/system.reflection.methodbase.getcurrentmethod.aspx
L'extrait fourni par Lex était un peu long, donc je signale la partie importante puisque personne d'autre n'a utilisé exactement la même technique:
string MethodName = new StackFrame(0).GetMethod().Name;
Cela devrait renvoyer des résultats identiques à la techniqueMethodBase.GetCurrentMethod().Name , mais cela vaut toujours la peine d'être souligné car je pourrais l'implémenter une fois dans sa propre méthode en utilisant l'index 1 pour la méthode précédente et l'appeler à partir d'un certain nombre de propriétés différentes. En outre, il ne renvoie qu'une image plutôt que le trace de pile entière:
private string GetPropertyName()
{ //.SubString(4) strips the property prefix (get|set) from the name
return new StackFrame(1).GetMethod().Name.Substring(4);
}
C'est aussi un one-liner ;)
Essayez ceci dans la méthode principale dans un programme de console vide:
MethodBase method = MethodBase.GetCurrentMethod();
Console.WriteLine(method.Name);
Sortie De La Console:Main
Oui certainement.
Si vous voulez qu'un objet soit manipulé, j'utilise en fait une fonction comme celle-ci:
public static T CreateWrapper<T>(Exception innerException, params object[] parameterValues) where T : Exception, new()
{
if (parameterValues == null)
{
parameterValues = new object[0];
}
Exception exception = null;
StringBuilder builder = new StringBuilder();
MethodBase method = new StackFrame(2).GetMethod();
ParameterInfo[] parameters = method.GetParameters();
builder.AppendFormat(CultureInfo.InvariantCulture, ExceptionFormat, new object[] { method.DeclaringType.Name, method.Name });
if ((parameters.Length > 0) || (parameterValues.Length > 0))
{
builder.Append(GetParameterList(parameters, parameterValues));
}
exception = (Exception)Activator.CreateInstance(typeof(T), new object[] { builder.ToString(), innerException });
return (T)exception;
}
Cette ligne:
MethodBase method = new StackFrame(2).GetMethod();
Remonte le cadre de la pile pour trouver la méthode d'appel, puis nous utilisons la réflexion pour obtenir des valeurs d'information de paramètre qui lui sont transmises pour une fonction de rapport d'erreur générique. Pour obtenir la méthode actuelle, utilisez simplement current stack frame (1) à la place.
Comme d'autres l'ont dit pour le nom des méthodes actuelles, vous pouvez également utiliser:
MethodBase.GetCurrentMethod()
Je préfère marcher sur la pile parce que si vous regardez en interne cette méthode, il crée simplement un StackCrawlMark de toute façon. Adresser directement la pile me semble plus clair
Post 4.5 vous pouvez maintenant utiliser le [CallerMemberNameAttribute] dans le cadre des paramètres de la méthode pour obtenir une chaîne du nom de la méthode-cela peut aider dans certains scénarios (mais vraiment dans l'exemple ci-dessus)
public void Foo ([CallerMemberName] string methodName = null)
Cela semblait être principalement une solution pour le support INotifyPropertyChanged où auparavant vous aviez des chaînes jonchées grâce à votre code d'événement.
EDIT: MethodBase est probablement un meilleur moyen d'obtenir la méthode dans laquelle vous vous trouvez (par opposition à toute la pile appelante). Je serais toujours préoccupé par l'inlining cependant.
Vous pouvez utiliser un StackTrace dans la méthode:
StackTrace st = new StackTrace(true);
Et le regard sur les cadres:
// The first frame will be the method you want (However, see caution below)
st.GetFrames();
Cependant, sachez que si la méthode est inline, vous ne serez pas à l'intérieur de la méthode que vous pensez de vous. Vous pouvez utiliser un attribut pour empêcher l'inlining:
[MethodImpl(MethodImplOptions.NoInlining)]
Comparer les façons d'obtenir le nom de la méthode - en utilisant une construction de synchronisation arbitraire dans LinqPad:
CODE
void Main()
{
// from http://blogs.msdn.com/b/webdevelopertips/archive/2009/06/23/tip-83-did-you-know-you-can-get-the-name-of-the-calling-method-from-the-stack-using-reflection.aspx
// and https://stackoverflow.com/questions/2652460/c-sharp-how-to-get-the-name-of-the-current-method-from-code
var fn = new methods();
fn.reflection().Dump("reflection");
fn.stacktrace().Dump("stacktrace");
fn.inlineconstant().Dump("inlineconstant");
fn.constant().Dump("constant");
fn.expr().Dump("expr");
fn.exprmember().Dump("exprmember");
fn.callermember().Dump("callermember");
new Perf {
{ "reflection", n => fn.reflection() },
{ "stacktrace", n => fn.stacktrace() },
{ "inlineconstant", n => fn.inlineconstant() },
{ "constant", n => fn.constant() },
{ "expr", n => fn.expr() },
{ "exprmember", n => fn.exprmember() },
{ "callermember", n => fn.callermember() },
}.Vs("Method name retrieval");
}
// Define other methods and classes here
class methods {
public string reflection() {
return System.Reflection.MethodBase.GetCurrentMethod().Name;
}
public string stacktrace() {
return new StackTrace().GetFrame(0).GetMethod().Name;
}
public string inlineconstant() {
return "inlineconstant";
}
const string CONSTANT_NAME = "constant";
public string constant() {
return CONSTANT_NAME;
}
public string expr() {
Expression<Func<methods, string>> ex = e => e.expr();
return ex.ToString();
}
public string exprmember() {
return expressionName<methods,string>(e => e.exprmember);
}
protected string expressionName<T,P>(Expression<Func<T,Func<P>>> action) {
// https://stackoverflow.com/a/9015598/1037948
return ((((action.Body as UnaryExpression).Operand as MethodCallExpression).Object as ConstantExpression).Value as MethodInfo).Name;
}
public string callermember([CallerMemberName]string name = null) {
return name;
}
}
Résultats
La Réflexion réflexion
Stacktrace stacktrace
Inlineconstant inlineconstant
Constante constante
Expr E = > E. expr ()
Exprmember exprmember
Callermember Principal
Method name retrieval: (reflection) vs (stacktrace) vs (inlineconstant) vs (constant) vs (expr) vs (exprmember) vs (callermember)
154673 ticks elapsed ( 15.4673 ms) - reflection
2588601 ticks elapsed (258.8601 ms) - stacktrace
1985 ticks elapsed ( 0.1985 ms) - inlineconstant
1385 ticks elapsed ( 0.1385 ms) - constant
1366706 ticks elapsed (136.6706 ms) - expr
775160 ticks elapsed ( 77.516 ms) - exprmember
2073 ticks elapsed ( 0.2073 ms) - callermember
>> winner: constant
Note que les méthodes expr
et callermember
ne sont pas tout à fait"correctes". Et là, vous voyez une répétition de un commentaire connexe {[6] } que la réflexion est ~15x plus rapide que stacktrace.
Que diriez-vous de ceci:
StackFrame frame = new StackFrame(1);
frame.GetMethod().Name; //Gets the current method name
MethodBase method = frame.GetMethod();
method.DeclaringType.Name //Gets the current class name
La façon simple de traiter est:
System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + System.Reflection.MethodBase.GetCurrentMethod().Name;
Si le système.La réflexion est incluse dans le bloc using:
MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + MethodBase.GetCurrentMethod().Name;
Je pense que vous devriez pouvoir obtenir cela en créant un StackTrace . Ou, comme @gde et @Lars Mæhlum mentionner, MethodBase. GetCurrentMethod()
Si vous n'avez besoin que du nom de chaîne de la méthode, vous pouvez utiliser des Expressions. Tu vois http://joelabrahamsson.com/entry/getting-property-and-method-names-using-static-reflection-in-c-sharp
Essayez ceci...
/// <summary>
/// Return the full name of method
/// </summary>
/// <param name="obj">Class that calls this method (use Report(this))</param>
/// <returns></returns>
public string Report(object obj)
{
var reflectedType = new StackTrace().GetFrame(1).GetMethod().ReflectedType;
if (reflectedType == null) return null;
var i = reflectedType.FullName;
var ii = new StackTrace().GetFrame(1).GetMethod().Name;
return string.Concat(i, ".", ii);
}
Je viens de le faire avec une simple classe statique:
using System.Runtime.CompilerServices;
.
.
.
public static class MyMethodName
{
public static string Show([CallerMemberName] string name = "")
{
return name;
}
}
Puis dans votre code:
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = MyMethodName.Show();
}
private void button2_Click(object sender, EventArgs e)
{
textBox1.Text = MyMethodName.Show();
}