在软件开发过程中,异常处理是一个重要的环节。良好的异常处理不仅能帮助开发者快速定位问题,还能提升用户体验。当用户在使用应用程序时遇到错误,一个清晰、友好的错误消息可以让用户更好地理解问题所在,从而向支持团队提供准确的信息,帮助快速解决问题。
然而,许多应用程序在遇到错误时,往往只显示一个通用的错误消息,如“技术问题,请与管理员联系”,这样的消息对用户来说并不友好,也不利于问题定位。为了解决这个问题,可以开发一个小工具,通过堆栈跟踪(stacktrace)和自定义属性来生成具体的错误消息。这样,就不需要在每个方法上都实现try-catch块。
要实现这种方法,需要在所有方法上放置一个自定义属性,该属性描述了方法的实际功能。这样,在异常发生时,可以为每个步骤生成消息。
需要注意的是,由于这个工具会从整个堆栈跟踪中提取方法描述,可能会暴露业务流程给最终用户。因此,这种方法更适合内部工具使用。如果不希望透露任何操作,可以不为特定方法指定描述。
在发布版本中,可能无法获取所有步骤。如果确实需要所有步骤,可以通过更改构建配置设置来实现。
具体操作如下:
Project-> Properties -> Build
Configuration = Release
Optimized Code = false (unchecked)
Advanced build settings: Debug Info: full
以下是主应用程序的源代码示例:
class Program {
static void Main(string[] args) {
try {
var loanValidator = new LoanValidator();
loanValidator.CheckLoanApplication();
} catch (Exception ex) {
string exceptionMessage = ExceptionHelper.GetDetailMessage(ex);
Console.Write(exceptionMessage);
Console.Read();
}
}
}
以下是自定义属性的源代码示例:
public class MethodDescription : Attribute {
public MethodDescription(string description) {
this.Description = description;
}
public string Description {
get;
set;
}
}
以下是贷款验证器的源代码示例:
public class LoanValidator {
[MethodDescription("Validating loan application")]
public bool CheckLoanApplication() {
bool isValidPersonalDetail = CheckPersonalDetail();
bool isValidEmploymentDetail = CheckEmploymentDetail();
bool isValidBankAccountDetail = CheckBankAccountDetail();
if (isValidPersonalDetail && isValidEmploymentDetail && isValidBankAccountDetail)
return true;
else
return false;
}
[MethodDescription("Checking personal detail")]
private bool CheckPersonalDetail() {
return true;
}
[MethodDescription("Checking employment detail")]
private bool CheckEmploymentDetail() {
return true;
}
[MethodDescription("Checking bank account detail")]
private bool CheckBankAccountDetail() {
CheckAnyOutstanding();
return true;
}
[MethodDescription("Checking outstanding detail from common web server")]
private bool CheckAnyOutstanding() {
throw new WebException();
return true;
}
}
public class ExceptionHelper {
public static string GetDetailMessage(Exception ex) {
StringBuilder messageBuilder = new StringBuilder();
Type attributeType = typeof(MethodDescription);
int step = 1;
var attributes = GetStackTraceSteps<MethodDescription>(ex);
if (attributes != null && attributes.Count > 0)
messageBuilder.AppendLine(string.Format("Sorry there is a problem while processing step {0}:", attributes.Count));
foreach (var attribute in attributes) {
messageBuilder.Append(string.Format("Step {0}: {1}", step.ToString(), attribute.Description));
messageBuilder.AppendLine();
step++;
}
messageBuilder.AppendLine();
string formatedMessage = string.Format("{0}Error Description : {1}", messageBuilder.ToString(), ex.Message);
return formatedMessage;
}
public static List<T> GetStackTraceSteps<T>(Exception ex) {
List<T> traceSteps = new List<T>();
Type attributeType = typeof(T);
StackTrace st = new StackTrace(ex);
if (st != null && st.FrameCount > 0) {
for (int index = st.FrameCount - 1; index >= 0; index--) {
var attribute = st.GetFrame(index).GetMethod().GetCustomAttributes(attributeType, false).FirstOrDefault();
if (attribute != null) {
traceSteps.Add((T)attribute);
}
}
}
return traceSteps;
}
}