.net - what - ¿Puedes usar la reflexión para encontrar el nombre del método que se está ejecutando actualmente?
what can be done with reflection in c# (13)
¿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
Como dice el título: ¿Puede la reflexión darle el nombre del método que se está ejecutando actualmente?
Me inclino a adivinar que no, debido al problema de Heisenberg. ¿Cómo se llama un método que le dirá el método actual sin cambiar el método actual? Pero espero que alguien pueda probar que estoy equivocado allí.
Actualizar:
- Parte 2: ¿Se podría utilizar para ver el código interno de una propiedad también?
- 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 solo puedo crear el cuadro exacto que necesito si quiero.
Para usar esto dentro de una propiedad, solo toma .Substring (4) para eliminar ''set_'' o ''get_''.
A partir de .NET 4.5, también puede usar [CallerMemberName]
Ejemplo: un conjunto 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á literales de cadena coincidentes en los sitios de llamadas, por lo que básicamente no hay sobrecarga de rendimiento.
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();
}
Comparando formas de obtener el nombre del método usando una construcción de tiempo arbitraria 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://.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://.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
reflejo de reflexión
pilatrace stacktrace
inlineconstant inlineconstant
constante constante
expr e => e.expr ()
exprmember exprmember
miembro de la llamada
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 los métodos expr
y callermember
no son del todo "correctos". Y allí verá una repetición de un comentario relacionado de que la reflexión es ~ 15 veces más rápida que stacktrace.
Creo que deberías poder conseguirlo creando un StackTrace . O, como lo mencionan @ edg y @ Lars Mæhlum , MethodBase. http://msdn.microsoft.com/en-us/library/system.reflection.methodbase.getcurrentmethod.aspx ()
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, aún me preocuparía la posibilidad de escribir.
Puede usar un StackTrace dentro del método:
StackTrace st = new StackTrace(true);
Y la mirada a los marcos:
// 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á en línea, no estará dentro del método que cree que es. Puede usar un atributo para evitar la alineación:
[MethodImpl(MethodImplOptions.NoInlining)]
El fragmento proporcionado por Lex fue un poco largo, así que estoy señalando la parte importante ya que nadie más utilizó la misma técnica exacta:
string MethodName = new StackFrame(0).GetMethod().Name;
Esto debería devolver resultados idénticos a la técnica MethodBase.GetCurrentMethod (). Name , pero aún vale la pena señalarlo porque podría implementar esto una vez en su propio método utilizando el índice 1 para el método anterior y llamarlo desde una cantidad de 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);
}
Es un trazador de líneas, también;)
La forma simple 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;
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);
}
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, realmente 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();
Camina hacia arriba en el marco de la pila para encontrar el método de llamada, luego usamos la reflexión para obtener valores de información de parámetros que se le pasan para una función de informe de error genérico. Para obtener el método actual, simplemente use el actual marco de pila (1).
Como otros han dicho para el nombre de los métodos actuales, también puedes usar:
MethodBase.GetCurrentMethod()
Prefiero caminar la pila porque si miras internamente ese método, simplemente crea un StackCrawlMark de todos modos. Dirigirse a la pila directamente me parece más claro
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 realmente 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 tenías cadenas almacenadas en todo tu código de evento.
Si solo necesita el nombre de la cadena del método, puede usar Expressions. Ver http://joelabrahamsson.com/entry/getting-property-and-method-names-using-static-reflection-in-c-sharp
System.Reflection.MethodBase.GetCurrentMethod().Name;
http://msdn.microsoft.com/en-us/library/system.reflection.methodbase.getcurrentmethod.aspx