Можете ли вы использовать отражение, чтобы найти имя выполняемого в данный момент метода?
Как гласит заголовок: Может ли отражение дать вам имя выполняемого в данный момент метода.
Я склонен не догадываться из-за проблемы Гейзенберга. Как вы вызываете метод, который сообщит вам текущий метод, не меняя его? Но я надеюсь, что кто-то может доказать, что я не прав там.
Обновить:
- Часть 2. Может ли это быть использовано для поиска в коде свойства?
- Часть 3: Каким будет представление?
Конечный результат
Я узнал о MethodBase.GetCurrentMethod(). Я также узнал, что я могу не только создавать трассировку стека, но и создавать только тот кадр, который мне нужен, если я хочу.
Чтобы использовать это внутри свойства, просто возьмите.Substring(4), чтобы удалить 'set_' или 'get_'.
20 ответов
Начиная с.NET 4.5 вы также можете использовать [CallerMemberName]
Пример: установщик свойства (для ответа на часть 2):
protected void SetProperty<T>(T value, [CallerMemberName] string property = null)
{
this.propertyValues[property] = value;
OnPropertyChanged(property);
}
public string SomeProperty
{
set { SetProperty(value); }
}
Компилятор будет предоставлять совпадающие строковые литералы на сайтах вызовов, так что в основном производительность не снижается.
System.Reflection.MethodBase.GetCurrentMethod().Name;
http://msdn.microsoft.com/en-us/library/system.reflection.methodbase.getcurrentmethod.aspx
Фрагмент, предоставленный Лексом, был немного длинным, поэтому я указываю на важную часть, так как никто другой не использовал точно такую же технику:
string MethodName = new StackFrame(0).GetMethod().Name;
Это должно вернуть идентичные результаты для метода MethodBase.GetCurrentMethod().Name, но на это все же стоит обратить внимание, потому что я мог бы реализовать это один раз в своем собственном методе, используя индекс 1 для предыдущего метода, и вызвать его из ряда различных свойств. Кроме того, он возвращает только один кадр вместо всей трассировки стека:
private string GetPropertyName()
{ //.SubString(4) strips the property prefix (get|set) from the name
return new StackFrame(1).GetMethod().Name.Substring(4);
}
Это тоже одна строка;)
Попробуйте это внутри метода Main в пустой консольной программе:
MethodBase method = MethodBase.GetCurrentMethod();
Console.WriteLine(method.Name);
Консольный вывод:Main
Сравнивая способы получения имени метода - используя произвольную временную конструкцию в LinqPad:
КОД
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://stackru.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://stackru.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;
}
}
РЕЗУЛЬТАТЫ
отражение отражение
стек трассировки стек трассировки
inlineconstant inlineconstant
постоянная константа
expr e => e.expr ()
exprmember exprmember
Главная
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
Обратите внимание, что expr
а также callermember
методы не совсем "правильные". И там вы видите повторение связанного комментария о том, что отражение в ~15 раз быстрее, чем трассировка стека.
Определенно да.
Если вы хотите, чтобы объект манипулировал, я на самом деле использую такую функцию:
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;
}
Эта строка:
MethodBase method = new StackFrame(2).GetMethod();
Идет вверх по стековому фрейму, чтобы найти вызывающий метод, затем мы используем отражение, чтобы получить значения информации о параметрах, передаваемые ему для общей функции сообщения об ошибках. Чтобы получить текущий метод, просто используйте вместо этого текущий стек (1).
Как уже говорили другие для имени текущего метода, вы также можете использовать:
MethodBase.GetCurrentMethod()
Я предпочитаю ходить по стеку, потому что если взглянуть внутренне на этот метод, он все равно просто создает StackCrawlMark. Обращение к стеку мне кажется более понятным
После 4.5 вы можете теперь использовать [CallerMemberNameAttribute] как часть параметров метода, чтобы получить строку с именем метода - это может помочь в некоторых сценариях (но на самом деле, скажем, в примере выше)
public void Foo ([CallerMemberName] string methodName = null)
Похоже, что это в основном решение для поддержки INotifyPropertyChanged, где ранее у вас были строки, засоренные на протяжении всего кода события.
РЕДАКТИРОВАТЬ: MethodBase, вероятно, лучший способ просто получить метод, в котором вы находитесь (в отличие от всего стека вызовов). Я все еще был бы обеспокоен по поводу встраивания однако.
Вы можете использовать StackTrace в методе:
StackTrace st = new StackTrace(true);
И посмотрите на кадры:
// The first frame will be the method you want (However, see caution below)
st.GetFrames();
Тем не менее, имейте в виду, что если метод встроен, вы не окажетесь внутри метода, которым вы себя считаете. Вы можете использовать атрибут для предотвращения встраивания:
[MethodImpl(MethodImplOptions.NoInlining)]
Простой способ разобраться:
System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + System.Reflection.MethodBase.GetCurrentMethod().Name;
Если System.Reflection включен в блок using:
MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + MethodBase.GetCurrentMethod().Name;
Для
Async
Методы, которые можно использовать:
//using System.Reflection;
var myMethodName = MethodBase
.GetCurrentMethod()
.DeclaringType
.Name
.Substring(1)
.Split('>')[0];
Чуть более гибкое решение для клиентов с 2021 года:
namespace my {
public struct notmacros
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string
whoami( [CallerMemberName] string caller_name = null)
{
if (string.IsNullOrEmpty(caller_name))
return "unknown";
if (string.IsNullOrWhiteSpace(caller_name))
return "unknown";
return caller_name;
}
}
} // my namespace
Применение
using static my.notmacros
// somewhere appropriate
var my_name = whoami() ;
Я сделал это, чтобы обрабатывать как асинхронные, так и простые вызовы старых методов.
В моем приложении он вызывается только из обработчиков исключений, поэтому производительность не вызывает беспокойства.
[MethodImpl(MethodImplOptions.NoInlining)]
public static string GetCurrentMethodName()
{
var st = new StackTrace();
var sf = st.GetFrame(1);
string name = sf.GetMethod().Name;
if (name.Equals("MoveNext"))
{
// We're inside an async method
name = sf.GetMethod().ReflectedType.Name
.Split(new char[] { '<', '>' }, StringSplitOptions.RemoveEmptyEntries)[0];
}
return name;
}
Как насчет этого:
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
Я думаю, что вы сможете получить это от создания StackTrace. Или, как упоминают @ edg и @ Lars Mæhlum, MethodBase. GetCurrentMethod ()
using System;
public class Program
{
public static void Main()
{
Console.WriteLine("1: {0} {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, System.Reflection.MethodBase.GetCurrentMethod().ReflectedType);
OtherMethod();
}
public static void OtherMethod()
{
Console.WriteLine("2: {0} {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, System.Reflection.MethodBase.GetCurrentMethod().ReflectedType);
}
}
Выход:
1: Main Program
2: OtherMethod Program
Я просто сделал это с помощью простого статического класса:
using System.Runtime.CompilerServices;
.
.
.
public static class MyMethodName
{
public static string Show([CallerMemberName] string name = "")
{
return name;
}
}
тогда в вашем коде:
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = MyMethodName.Show();
}
private void button2_Click(object sender, EventArgs e)
{
textBox1.Text = MyMethodName.Show();
}
Добавьте где-нибудь этот метод и вызовите его без параметра!
public static string GetCurrentMethodName([System.Runtime.CompilerServices.CallerMemberName] string name = "")
{
return name;
}
Попробуй это...
/// <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);
}
Вот что я использую в статическом вспомогательном классе для своих асинхронных методов.
public static string GetMethodName(string rawName)
{
return rawName.Substring(1, rawName.IndexOf('>') - 1);
}
Вызов:
string methodName = StringExtensionMethods.GetMethodName(MethodBase.GetCurrentMethod().ReflectedType.Name ?? "");
ХТН
Если вам нужно только строковое имя метода, вы можете использовать выражения. Смотрите http://joelabrahamsson.com/entry/getting-property-and-method-names-using-static-reflection-in-c-sharp
new StackTrace().ToString().Split("\r\n",StringSplitOptions.RemoveEmptyEntries)[0].Replace("at ","").Trim()