就像标题说的:反射能否给你当前正在执行的方法的名称。
由于海森堡问题,我倾向于猜测不是。你如何调用一个方法来告诉你当前的方法而不改变当前的方法是什么?但我希望有人能证明我错了。
更新:
第 2 部分:这也可以用来查看属性的内部代码吗?
第 3 部分:表演会是什么样的?
最终结果 我了解了 MethodBase.GetCurrentMethod()。我还了解到,我不仅可以创建堆栈跟踪,还可以根据需要只创建我需要的确切帧。
要在属性中使用它,只需使用 .Substring(4) 删除“set_”或“get_”。
对于可以使用的非async
方法
System.Reflection.MethodBase.GetCurrentMethod().Name;
https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.getcurrentmethod
请记住,对于 async
方法,它将返回“MoveNext”。
从 .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); }
}
编译器将在调用站点提供匹配的字符串文字,因此基本上没有性能开销。
StackFrame(1)
方法进行日志记录,在 Jitter 决定开始内联之前,该方法似乎有效。出于性能原因,我不想添加属性来防止内联。使用 [CallerMemberName]
方法解决了该问题。谢谢!
OnPropertyChanged("SomeProperty")
而不是 OnPropertyChanged("SetProperty")
Lex 提供的片段有点长,所以我指出了重要的部分,因为没有其他人使用完全相同的技术:
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 中使用 arbitrary timing construct:
代码
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;
}
}
结果
反射 反射
堆栈跟踪堆栈跟踪
内联常量内联常量
常数常数
表达式 => e.expr()
exprmember exprmember
callermember 主要
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
方法并不完全“正确”。在那里你会看到 a related comment 的重复,反射比堆栈跟踪快约 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)]
new StackTrace(true)
而不是 new StackTrace(false)
。将其设置为 true
将导致堆栈跟踪尝试捕获文件名、行号等,这可能会使此调用变慢。否则,一个很好的答案
简单的处理方法是:
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];
这个怎么样:
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
为了处理异步和普通的旧方法调用,我这样做了。
在我的应用程序中,它只是从异常处理程序中调用,所以性能不是问题。
[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;
}
从 2021 年到 2022 年,为客户提供更具弹性的解决方案:
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() ;
实际演示的 .NET 小提琴链接:
https://dotnetfiddle.net/moK73n
请注意编译器要求:.NET 6
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);
}
new StackTrace().ToString().Split("\r\n",StringSplitOptions.RemoveEmptyEntries)[0].Replace("at ","").Trim()
async
方法中,您很可能会得到“MoveNext”作为方法名称。