Up to this point, I've been speaking of the debug-only Assert method found in .NET's System.Diagnostics.Debug class. Now would be a good time to mention the Debug class's twin sister, System.Diagnostics.Trace. Both provide excellent logging facilities (described in Chapter 5) as well as the Assert method. In fact, both classes have the exact same set of methods. The one difference is that methods from the Debug class only work when the code is compiled in Debug mode, but the methods from Trace work in either Debug or Release mode. So when I talk of debug-only asserts, I'm referring to the Debug class. If you used the Trace class, then your asserts would appear all the time, not just in Debug mode.
Why would you ever use the Trace class's Assert method? What value is an assert in Release mode? Good question. Personally, I've never found much use for the Trace version of Assert. An assert is meaningful to the developer; it is not usually meaningful to an end user. Users can appreciate error messages like "The file you selected is invalid. Please select a different file", because even though that error message doesn't describe the problem very well, it at least identifies what to do. On the other hand, the assert in Figure 4-2 would be excellent for a developer, but do you think your average user would understand what it means?
Also, the dialog box displayed by the Assert function gives the options to debug, ignore the problem, or abort. You probably don't want to present those options to your users. Does your average user have the necessary skills, tools, and source code needed to debug your program? Does she have the necessary information to decide whether the assert can safely be ignored or whether she should abort? Probably not.
If you encounter a severe error in Release mode that cannot be hidden by the error handling code, you are best served by displaying a regular message box with a human readable description of the problem and a simple OK button. Maybe that error message is merely, "A fatal exception occurred. Please contact tech support." That's still preferable to showing the user an assert. At least this error message tells the user what to do. All in all, I'd recommend avoiding the Trace class's Assert method. The Trace class is primarily used for logging, not asserting.
In any case, regardless of whether you use the Debug or Trace class, the Assert function comes in three flavors:
Assert(bool condition) Assert(bool condition, string description) Assert(bool condition, string description, string extendedDescription)
They all behave similarly—they check whether the condition is true and display a message if it isn't. The difference is what they display. All three versions display a stack trace leading up the assert, and that's all you see with the first overload. The second overload shows a stack trace, too; but it also displays a user- defined string to describe the assert. For example, maybe you want the assert to show you text like "Error: input parameter was null" so that you can immediately identify the problem without having to launch the debugger. Finally, the third version is useful if you want the assert to show both a short description ("The parameter was null") as well as a longer description ("This error usually means that the calling function failed to do thus-and-so. To fix it, do blah blah blah.") I normally use the second version, but the choice boils down to how much you want to type.
bool didItWork = DoStuffAndReturnTrueIfItWorked(); Debug.Assert(didItWork);
Anyone who is new to asserts will be tempted to save a line of code by rewriting that as
Test that code in Debug mode and everything works fine. But it will produce a tremendous error in Release mode, where debug asserts are compiled away into nothingness to maximize performance. So even though this code works great in Debug mode, when running in Release mode, the DoStuffAndReturnTrueIfItWorked function will never be executed because the assert will make it disappear.
Make sure that you don't put any function that does important stuff by side effect in an assert. Put that function outside the assert, and then call the Assert function on the return value.