在写通用组件的时候,我们需要关心程序运行的堆栈信息。堆栈信息是什么?
堆栈是一种执行“后进先出”算法的数据结构。程序的函数调用是用堆栈实现的。
在程序中,每次调用一个函数,就会生成一个堆栈帧,并push进入堆栈中。每一次结束一个函数调用,就会从堆栈中pop一个堆栈帧。
在.Net Framework中,定义了StackTrace类,它可以获取当前堆栈的每一帧的信息。
它的构造函数:
//默认不获取文件信息
StackTrace();
//是否获取文件信息,包括文件名,行号等
StackTrace(bool fNeedFileInfo);
//可以指定跳过帧的数量
StackTrace(int skipFrames);
StackTrace(int skipFrames, bool fNeedFileInfo);
//根据提供的Exception对象,生成堆栈信息
StackTrace(Exception e);
StackTrace(Exception e, bool fNeedFileInfo);
StackTrace(Exception e, int skipFrames);
StackTrace(Exception e, int skipFrames, bool fNeedFileInfo);
它的属性:
//返回堆栈信息的帧的数量
int FrameCount;
它的方法:
//返回堆栈信息的所有帧
StackFrame[] GetFrames();
//根据帧的索引,返回帧
StackFrame GetFrame(int index);
StackFrame类提供的方法:
int GetFileLineNumber();
int GetFileColumnNumber();
string GetFileName();
MethodBase GetMethod();
做一个简单的练习:
static void Main(string[] args)
{
StackTraceSample stackTraceSample = new StackTraceSample();
try
{
stackTraceSample.MyPublicMethod();
}
catch (Exception e)
{
// Create a StackTrace that captures
// filename, line number, and column
// information for the current thread.
StackTrace st = new StackTrace(e, true);
string stackIndent = "";
for (int i = 0; i < st.FrameCount; i++)
{
// Note that high up the call stack, there is only
// one stack frame.
StackFrame sf = st.GetFrame(i);
Console.WriteLine();
Console.WriteLine(stackIndent + " Method: {0}",
sf.GetMethod());
Console.WriteLine(stackIndent + " File: {0}",
sf.GetFileName());
Console.WriteLine(stackIndent + " Line Number: {0}"
, sf.GetFileLineNumber());
stackIndent += " ";
}
}
Console.ReadKey();
}
public class StackTraceSample
{
public void MyPublicMethod()
{
MyProtectedMethod();
}
protected void MyProtectedMethod()
{
MyInternalClass mic = new MyInternalClass();
mic.ThrowsException();
}
class MyInternalClass
{
public void ThrowsException()
{
try
{
throw new Exception("A problem was encountered.");
}
catch (Exception)
{
// Create a StackTrace that captures filename,
// line number and column information.
StackTrace st = new StackTrace(true);
string stackIndent = "";
for (int i = 0; i < st.FrameCount; i++)
{
// Note that at this level, there are four
// stack frames, one for each method invocation.
StackFrame sf = st.GetFrame(i);
Console.WriteLine();
Console.WriteLine(stackIndent + " Method: {0}",
sf.GetMethod());
Console.WriteLine(stackIndent + " File: {0}",
sf.GetFileName());
Console.WriteLine(stackIndent + " Line Number: {0}"
,sf.GetFileLineNumber());
stackIndent += " ";
}
throw;
}
}
}
}
运行的结果:
我们这里的堆栈信息并不能获取每一个函数在调用的时候,传递的参数值,对于有这种需求的程序员,可以考虑使用AOP编程的方式编写一个MethodContextLogger组件,这个组件可以获取方法的出入的参数值,并记录下来。关于如何实现AOP编程,可以参考我之前的文章。
-纸上得来终觉浅,绝知此事要躬行-