Recipe 5.5. Writing a More Flexible StackTrace ClassProblemYou have a StackTrace object that contains a listing of stack frames. You need to iterate through these stack frames as if you were using a Collection-type object. SolutionWrap the public interface of a StackTrace object to look like a Collection-type object. The StackTraceList class shown in Example 5-1 implements this design pattern. Example 5-1. Writing a More Flexible StackTrace Class
DiscussionThis recipe uses the System.Diagnostics.StackTrace object to obtain a list of stack frames, which it then provides to the user. The StackTrace class provides a convenient way to obtain a stack trace, an exception object, or a specific thread from the current point in code. Unfortunately, the StackTrace provides only a very simplified way to get at each stack frame. It would be much better if the StackTrace object operated like a collection. To make this happen, you can create an intermediate object called StackTraceList that inherits from StackTrace and implements the ICloneable, IList, ICollection, and IEnumerable interfaces. The constructors for the StackTraceList class mimic the StackTrace constructors. Each StackTraceList constructor passes its work along to the base class using the base keyword: public StackTraceList( ) : base( ) Each StackTraceList constructor contains a call to the private method, Init-InternalFrameArray. This private method copies all of the individual StackFrame objects from the base StackTrace object into a private field of type StackFrame[] called internalFrameArray. The StackTraceList uses the internalFrameArray field as a convenient storage mechanism for each individual StackFrame object; in addition, you get a free implementation of the IEnumerator interface. It also makes it easier to make the StackTraceList class look and feel more like an array than a StackTrace object. Another useful method added to the StackTraceList class is the public GetFrameAsString method. This method accepts an index of a specific StackFrame object in the internalFrameArray field. From this StackFrame object, it constructs a string similar to the string output for each StackFrame. The methods implemented from the IList, ICollection, and IEnumerable interfaces forward their calls on to the internalFrameArray field, which implements the same interfacesthrowing the NotSupportedException for most of these interface methods. The StackTrace object can now be used as if it were a collection, through the intermediate StackTraceList object. To obtain a StackTraceList object for the current point in code, use the following code: StackTraceList arrStackTrace = new StackTraceList( ); To display a portion or all of the stack trace, use the following code: // Display the first stack frame. Console.WriteLine(arrStackTrace[0].ToString( )); // Display all stack frames. foreach (StackFrame SF in arrStackTrace) { Console.WriteLine("stackframe: " + SF.ToString( )); } To obtain a StackTraceList object from a thrown exception, use the following code: … catch (Exception e) { StackTraceList EST = new StackTraceList(e, true); Console.WriteLine("TOSTRING: " + Environment.NewLine + EST.ToString( )); foreach (StackFrame SF in EST) { Console.WriteLine(SF.ToString( )); } } To copy the StackFrame objects to a new array, use the following code: StackFrame[] myNewArray = new StackFrame[arrStackTrace.Count]; arrStackTrace.CopyTo(myNewArray, 0); You will notice that the first StackFrame object in the stack trace contains something like the following: at AdapterPattern.StackTraceList..ctor( ) This is actually the constructor call to the StackTraceList object. This information is usually not necessary to display and can be removed quite easily. When creating the StackTraceList object, pass in an integer one as an argument to the constructor. This will force the first stack frame (the one containing the call to the StackTraceList constructor) to be discarded: StackTraceList arrStackTrace = new StackTraceList(1); You should note that the Add, Insert, Remove, and RemoveAt methods on the IList interface of an Array type throw the NotSupportedException, because an array is fixed in length and these methods will alter the length of the array. See AlsoSee the "StackTrace Class" and "IList Interface" topics in the MSDN documentation. Also see the "Adapter Design Pattern" chapter in Design Patterns (Addison-Wesley). |