¿Puedes utilizar la reflexión para encontrar el nombre del método que se está ejecutando actualmente?

StackOverflow https://stackoverflow.com/questions/44153

  •  09-06-2019
  •  | 
  •  

Pregunta

Como dice el título:¿Puede la reflexión darle el nombre del método que se está ejecutando actualmente?

Me inclino a suponer que no, debido al problema de Heisenberg.¿Cómo se llama a un método que le indicará el método actual sin cambiar cuál es el método actual?Pero espero que alguien pueda demostrar que estoy equivocado.

Actualizar:

  • Parte 2:¿Podría usarse esto también para buscar una propiedad dentro del código?
  • Parte 3:¿Cómo sería la actuación?

Resultado final
Aprendí sobre MethodBase.GetCurrentMethod().También aprendí que no solo puedo crear un seguimiento de pila, sino que también puedo crear solo el marco exacto que necesito si así lo deseo.

Para usar esto dentro de una propiedad, simplemente tome .Substring(4) para eliminar 'set_' o 'get_'.

¿Fue útil?

Solución

A partir de .NET 4.5 también puedes usar [Nombre del miembro que llama]

Ejemplo:un fijador de propiedades (para responder a la parte 2):

    protected void SetProperty<T>(T value, [CallerMemberName] string property = null)
    {
        this.propertyValues[property] = value;
        OnPropertyChanged(property);
    }

    public string SomeProperty
    {
        set { SetProperty(value); }
    }

El compilador proporcionará cadenas literales coincidentes en los sitios de llamada, por lo que básicamente no hay sobrecarga de rendimiento.

Otros consejos

El fragmento proporcionado por Lex fue un poco largo, así que señalo la parte importante ya que nadie más usó exactamente la misma técnica:

string MethodName = new StackFrame(0).GetMethod().Name;

Esto debería devolver resultados idénticos a los MethodBase.GetCurrentMethod().Nombre técnica, pero aún así vale la pena señalarlo porque podría implementar esto una vez en su propio método usando el índice 1 para el anterior método y llamarlo desde varias propiedades diferentes.Además, solo devuelve un fotograma en lugar de todo el seguimiento de la pila:

private string GetPropertyName()
{  //.SubString(4) strips the property prefix (get|set) from the name
    return new StackFrame(1).GetMethod().Name.Substring(4);
}

También es una sola línea;)

Pruebe esto dentro del método Main en un programa de consola vacío:

MethodBase method = MethodBase.GetCurrentMethod();
Console.WriteLine(method.Name);

Salida de consola:
Main

Sí definitivamente.

Si quieres manipular un objeto, en realidad uso una función como esta:

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;
}

Esta línea:

MethodBase method     = new StackFrame(2).GetMethod();

Sube por el marco de la pila para encontrar el método de llamada y luego usamos la reflexión para obtener los valores de información de los parámetros que se le pasan para una función genérica de informe de errores.Para obtener el método actual, simplemente use el marco de pila actual (1) en su lugar.

Como otros han dicho para el nombre del método actual, también puedes usar:

MethodBase.GetCurrentMethod()

Prefiero recorrer la pila porque si miras internamente ese método, de todos modos simplemente crea un StackCrawlMark.Abordar el Stack directamente me parece más claro

En la publicación 4.5, ahora puede usar [CallerMemberNameAttribute] como parte de los parámetros del método para obtener una cadena del nombre del método; esto puede ayudar en algunos escenarios (pero en realidad, digamos en el ejemplo anterior).

public void Foo ([CallerMemberName] string methodName = null)

Esto parecía ser principalmente una solución para el soporte de INotifyPropertyChanged donde anteriormente había cadenas esparcidas por todo el código de evento.

Comparando formas de obtener el nombre del método - usando un construcción de tiempo arbitrario en LinqPad:

CÓDIGO

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;
    }
}

RESULTADOS

reflexiónreflexión

seguimiento de pilaseguimiento de pila

constante en líneaconstante en línea

constanteconstante

exprése => e.expr()

exprmiembroexprmiembro

miembro que llamaPrincipal

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

Tenga en cuenta que el expr y callermember Los métodos no son del todo "correctos".Y ahí ves una repetición de un comentario relacionado esa reflexión es ~ 15 veces más rápida que el seguimiento de pila.

EDITAR:MethodBase es probablemente una mejor manera de obtener el método en el que se encuentra (a diferencia de toda la pila de llamadas).Sin embargo, todavía me preocuparía la incorporación.

Puedes usar un StackTrace dentro del método:

StackTrace st = new StackTrace(true);

Y el vistazo a los fotogramas:

// The first frame will be the method you want (However, see caution below)
st.GetFrames();

Sin embargo, tenga en cuenta que si el método está integrado, no estará dentro del método que cree estar.Puede utilizar un atributo para evitar la inserción:

[MethodImpl(MethodImplOptions.NoInlining)]

La forma sencilla de tratar es:

System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + System.Reflection.MethodBase.GetCurrentMethod().Name;

Si System.Reflection está incluido en el bloque de uso:

MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + MethodBase.GetCurrentMethod().Name;

¿Qué tal esto?

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

Creo que deberías poder conseguirlo creando un seguimiento de pila.O como @borde y @Lars Mahlum mencione, MethodBase.Obtener método actual()

Si necesita solo el nombre de la cadena del método, puede usar Expresiones.Ver http://joelabrahamsson.com/entry/getting-property-and-method-names-using-static-reflection-in-c-sharp

Prueba esto...

    /// <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);
    }

Acabo de hacer esto con una clase estática simple:

using System.Runtime.CompilerServices;
.
.
.
    public static class MyMethodName
        {
            public static string Show([CallerMemberName] string name = "")
            {
                return name;
            }
        }

luego en tu código:

private void button1_Click(object sender, EventArgs e)
        {
            textBox1.Text = MyMethodName.Show();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            textBox1.Text = MyMethodName.Show();
        }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top