如何判断哪个方法调用了相同的事件?
昨日MSDN有人询问我一个问题,假设某个类中有两个事件定义(它们的委托完全一致,并且同时绑定到一个方法)。假设C#代码如下:
namespace CSharp { class A { public event Action MyEvent; public event Action MyEvent2; public void Call() { MyEvent(); } public void Call2() { MyEvent2(); } } class Program { static void Main(string[] args) { A a = new A(); a.MyEvent += a_MyEvent; a.MyEvent2 += a_MyEvent; a.Call(); a.Call2(); } static void a_MyEvent() { //如何知道是Call还是Call2在调用此事件绑定方法呢? } } }
我没有想到用StackTrance类就可以!
查阅MSDN,并且通过一些简单的示例代码我得知这个类就是提供断点调试时候“Trace”列表自动跟踪作用的。因此代码可以这样写:
static void a_MyEvent() { StackTrace st = new StackTrace(); Console.WriteLine(st.GetFrames()[1].GetMethod().Name); }
StackTrace是一个跟踪状态类,跟踪当前方法是何种方法,以及调用该方法的上层方法,上上层方法。那么我们可以想:Main调用了Call,而Call又调用了这个事件绑定方法。因此一次调用跟踪这个trace就会产生“Main","Call"和"a_MyEvent"(两次调用,总共产生6次)——由于是”栈“,因此存放顺序是"a_MyEvent"最上,“Main"最下。这些个跟踪的方法存于StackFrames属性中(每一个StackFrame也是一个类,表示一个“跟踪帧”),作为集合出现。其标号分别是0,1,2……即当前(声明StackTrace那一刻开始跟踪的方法为0,上级方法1,再上级为2……以此类推)。因此此处为1。
要证明这点也不难,可以完全打印:
namespace CSharp { class A { public event Action MyEvent; public event Action MyEvent2; public void Call(int i) { MyEvent(); } public void Call2() { MyEvent2(); } } class Program { static void Main(string[] args) { A a = new A(); a.MyEvent += a_MyEvent; a.MyEvent2 += a_MyEvent; a.Call(1); a.Call2(); } static void a_MyEvent() { StackTrace st = new StackTrace(); foreach (var item in st.GetFrames()) { Console.WriteLine(item.GetMethod().Name); } } } }
你也可以用StackFrame(单个帧)去跟踪当前方法,譬如:
static void a_MyEvent() { StackFrame sf = new StackFrame(); Console.WriteLine(sf.GetMethod().Name); }
此时输出的是a_MyEvent,如果你需要跟踪其父类(上级),那么你可以在构造函数中写入数字(表示从当前的帧数往上追溯其第N个父类方法)。本题是追溯其第一个父类,自然写1:
static void a_MyEvent() { StackFrame sf = new StackFrame(1); Console.WriteLine(sf.GetMethod().Name); }
所以总结如下:
StackTrace包含一个完整跟踪的StackFrame列表,通过StackTrace寻找帧数直接可以通过索引(0,1,2)。其中0表示当前方法(定义StackTrace开始跟踪的那一个方法),1表示其父类方法……以此类推。
StackFrame仅包含当前开始跟踪的那个方法(=MethodBase.GetCurrentMethod().Name),因此如果要寻找其父类,必须在构造函数中填入某个数字(表示要寻找其第N个父类方法)。