Você pode usar a reflexão para encontrar o nome do método atualmente em execução?
-
09-06-2019 - |
Pergunta
Como o título diz:A reflexão pode fornecer o nome do método atualmente em execução.
Estou inclinado a supor que não, por causa do problema de Heisenberg.Como você chama um método que informa o método atual sem alterar qual é o método atual?Mas espero que alguém possa provar que estou errado.
Atualizar:
- Parte 2:Isso também poderia ser usado para procurar uma propriedade dentro do código?
- Parte 3:Como seria o desempenho?
Resultado final
Aprendi sobre MethodBase.GetCurrentMethod().Também aprendi que não só posso criar um rastreamento de pilha, como também posso criar apenas o quadro exato de que preciso, se quiser.
Para usar isso dentro de uma propriedade, basta pegar um .Substring(4) para remover o 'set_' ou 'get_'.
Solução
A partir do .NET 4.5 você também pode usar [nome do membro chamador]
Exemplo:um configurador de propriedades (para responder à parte 2):
protected void SetProperty<T>(T value, [CallerMemberName] string property = null)
{
this.propertyValues[property] = value;
OnPropertyChanged(property);
}
public string SomeProperty
{
set { SetProperty(value); }
}
O compilador fornecerá literais de string correspondentes nos locais de chamada, portanto, basicamente não há sobrecarga de desempenho.
Outras dicas
System.Reflection.MethodBase.GetCurrentMethod().Name;
http://msdn.microsoft.com/en-us/library/system.reflection.methodbase.getcurrentmethod.aspx
O trecho fornecido por Lex foi um pouco longo, então estou apontando a parte importante, já que ninguém mais usou exatamente a mesma técnica:
string MethodName = new StackFrame(0).GetMethod().Name;
Isso deve retornar resultados idênticos para o MethodBase.GetCurrentMethod().Nome técnica, mas ainda vale a pena ressaltar porque eu poderia implementar isso uma vez em seu próprio método usando o índice 1 para o anterior método e chamá-lo de várias propriedades diferentes.Além disso, ele retorna apenas um quadro em vez de todo o rastreamento de pilha:
private string GetPropertyName()
{ //.SubString(4) strips the property prefix (get|set) from the name
return new StackFrame(1).GetMethod().Name.Substring(4);
}
É uma frase única também ;)
Tente isso dentro do método Main em um programa de console vazio:
MethodBase method = MethodBase.GetCurrentMethod();
Console.WriteLine(method.Name);
Saída do console:
Main
Sim definitivamente.
Se você quiser que um objeto seja manipulado, eu uso uma função 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;
}
Está linha:
MethodBase method = new StackFrame(2).GetMethod();
Percorre o quadro da pilha para encontrar o método de chamada e, em seguida, usamos a reflexão para obter os valores das informações dos parâmetros passados a ele para uma função genérica de relatório de erros.Para obter o método atual, basta usar o quadro de pilha atual (1).
Como já foi dito sobre o nome dos métodos atuais, você também pode usar:
MethodBase.GetCurrentMethod()
Eu prefiro percorrer a pilha porque se olhar internamente para esse método, ele simplesmente cria um StackCrawlMark de qualquer maneira.Abordar a pilha diretamente parece mais claro para mim
Após a versão 4.5, agora você pode usar o [CallerMemberNameAttribute] como parte dos parâmetros do método para obter uma string do nome do método - isso pode ajudar em alguns cenários (mas, digamos, no exemplo acima)
public void Foo ([CallerMemberName] string methodName = null)
Esta parecia ser principalmente uma solução para suporte a INotifyPropertyChanged, onde anteriormente você tinha strings espalhadas por todo o código do evento.
Comparando maneiras de obter o nome do método - usando um construção de temporização arbitrária no 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
reflexãoreflexão
rastreamento de pilharastreamento de pilha
constante embutidaconstante embutida
constanteconstante
expressãoe => e.expr()
exprmembroexprmembro
membro chamadorPrincipal
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
Observe que o expr
e callermember
métodos não são exatamente "certos".E aí você vê uma repetição de um comentário relacionado essa reflexão é cerca de 15x mais rápida que o stacktrace.
EDITAR:MethodBase é provavelmente a melhor maneira de obter apenas o método em que você está (em oposição a toda a pilha de chamadas).Eu ainda estaria preocupado com o inlining.
Você pode usar um StackTrace dentro do método:
StackTrace st = new StackTrace(true);
E olhem os quadros:
// The first frame will be the method you want (However, see caution below)
st.GetFrames();
No entanto, esteja ciente de que se o método estiver embutido, você não estará dentro do método que pensa que está.Você pode usar um atributo para evitar inlining:
[MethodImpl(MethodImplOptions.NoInlining)]
A maneira simples de lidar é:
System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + System.Reflection.MethodBase.GetCurrentMethod().Name;
Se System.Reflection estiver incluído no bloco using:
MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + MethodBase.GetCurrentMethod().Name;
Que tal isso:
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
Acho que você deveria conseguir isso criando um StackTrace.Ou, como @borda e @Lars Mæhlum mencionar, MethodBase.Método GetCurrent()
Se você precisar apenas do nome da string do método, poderá usar Expressões.Ver http://joelabrahamsson.com/entry/getting-property-and-method-names-using-static-reflection-in-c-sharp
Experimente isso...
/// <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);
}
Acabei de fazer isso com uma classe estática simples:
using System.Runtime.CompilerServices;
.
.
.
public static class MyMethodName
{
public static string Show([CallerMemberName] string name = "")
{
return name;
}
}
então no seu 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();
}