internal static class ExceptionResolver
{
private class CategoryStorage : Dictionary<string, CategoryStorage>
{
private Type excp;
public Type Exception
{
get
{
return excp;
}
set
{
excp = value;
}
}
}
private class RootCategoryStorage : CategoryStorage
{
private Dictionary<ErrorCode, Type> errorCodeSpecific = new Dictionary<ErrorCode, Type>();
public Dictionary<ErrorCode, Type> ErrorCodeSpecific => errorCodeSpecific;
}
private static Dictionary<ErrorCode, Type> resolution;
private static Dictionary<ErrorCode, XmlNode> extraInfo;
static ExceptionResolver()
{
resolution = new Dictionary<ErrorCode, Type>();
extraInfo = new Dictionary<ErrorCode, XmlNode>();
RootCategoryStorage rootCategoryStorage = new RootCategoryStorage();
Type[] types = Assembly.GetExecutingAssembly().GetTypes();
foreach (Type type in types)
{
if ((object)type != typeof(A3200Exception) && !type.IsSubclassOf(typeof(A3200Exception)))
{
continue;
}
object[] customAttributes = type.GetCustomAttributes(typeof(CategoryAttribute), inherit: false);
for (int j = 0; j < customAttributes.Length; j++)
{
CategoryAttribute categoryAttribute = (CategoryAttribute)customAttributes[j];
if (categoryAttribute.ErrorCode.HasValue)
{
rootCategoryStorage.ErrorCodeSpecific[categoryAttribute.ErrorCode.Value] = type;
continue;
}
CategoryStorage categoryStorage = rootCategoryStorage;
ExceptionCategory[] names = categoryAttribute.Names;
for (int k = 0; k < names.Length; k++)
{
ExceptionCategory exceptionCategory = names[k];
if (exceptionCategory != 0)
{
if (!categoryStorage.ContainsKey(exceptionCategory.ToString()))
{
categoryStorage[exceptionCategory.ToString()] = new CategoryStorage();
}
categoryStorage = categoryStorage[exceptionCategory.ToString()];
}
}
categoryStorage.Exception = type;
}
}
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(InfoResources.errorCodesInfo);
FieldInfo[] fields = typeof(ErrorCode).GetFields(BindingFlags.Static | BindingFlags.Public);
foreach (FieldInfo fieldInfo in fields)
{
ErrorCode key = (ErrorCode)fieldInfo.GetValue(null);
XmlNode xmlNode = xmlDocument.SelectSingleNode("/descendant::error[@name='" + fieldInfo.Name + "']");
if (xmlNode == null)
{
resolution[(ErrorCode)fieldInfo.GetValue(null)] = rootCategoryStorage.Exception;
continue;
}
if (rootCategoryStorage.ErrorCodeSpecific.ContainsKey(key))
{
resolution[key] = rootCategoryStorage.ErrorCodeSpecific[key];
}
else
{
CategoryStorage categoryStorage2 = rootCategoryStorage;
Type exception = rootCategoryStorage.Exception;
foreach (XmlNode item in xmlNode.SelectNodes("category"))
{
string value = item.Attributes["name"].Value;
if (!value.Equals(ExceptionCategory.Root.ToString()))
{
if (!categoryStorage2.ContainsKey(value))
{
break;
}
categoryStorage2 = categoryStorage2[value];
if ((object)categoryStorage2.Exception != null)
{
exception = categoryStorage2.Exception;
}
}
}
resolution[key] = exception;
}
extraInfo[key] = xmlNode;
}
}
public static void ThrowInvalidCallbackArguments(Controller controller)
{
ResolveThrow(controller, new ErrorData(95, 18));
}
public static void ResolveThrow(ErrorData err)
{
string errorMessage = GetErrorMessage(err);
ResolveThrow(err, errorMessage);
}
public static Exception Resolve(Controller controller, ErrorData err, string message)
{
ErrorCode code = (ErrorCode)err.Code;
if (code == ErrorCode.NoError)
{
return null;
}
if (!resolution.ContainsKey(code))
{
throw new ArgumentOutOfRangeException("err", Resources.InvalidErrorInformation);
}
if (controller == null)
{
return (Exception)Activator.CreateInstance(resolution[code], BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.CreateInstance, null, new object[3]
{
err,
extraInfo[code],
message
}, null);
}
return (Exception)Activator.CreateInstance(resolution[code], BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.CreateInstance, null, new object[4]
{
controller,
err,
extraInfo[code],
message
}, null);
}
public static void ResolveThrow(ErrorData err, string message)
{
Exception ex = Resolve(null, err, message);
if (ex != null)
{
throw ex;
}
}
public static void ResolveThrow(Controller controller, ErrorData err)
{
string errorMessage = GetErrorMessage(err);
ResolveThrow(controller, err, errorMessage);
}
public static void ResolveThrow(Controller controller, ErrorData err, string message)
{
Exception ex = Resolve(controller, err, message);
if (ex != null)
{
throw ex;
}
}
public static string GetErrorMessage(ErrorData errorData)
{
return GetErrorMessage(errorData, null);
}
public static string GetErrorMessage(ErrorData errorData, string extendedErrorMessage)
{
StringBuilder stringBuilder = new StringBuilder(512);
Wrapper.AerNewErrGetMessage(errorData, extendedErrorMessage, stringBuilder, stringBuilder.Capacity, CultureInfo.CurrentUICulture.LCID);
return stringBuilder.ToString();
}
public static ErrorData CreateErrorStructure(int code)
{
return new ErrorData(code, 0);
}
public static ErrorData CreateErrorStructure(ErrorCode code)
{
return new ErrorData((int)code, 0);
}
public static ErrorCode GetErrorCode(ErrorData errorData)
{
return (ErrorCode)errorData.Code;
}
public static string GetCompilerErrorMessage(CompilerHandle compilerHandle, int errorNumber)
{
StringBuilder stringBuilder = new StringBuilder(512);
ResolveThrow(Wrapper.AerCompilerErrGetText(compilerHandle.Value, errorNumber, stringBuilder, stringBuilder.Capacity));
return stringBuilder.ToString();
}
public static CompilerErrorType GetErrorKind(ErrorData errorCode)
{
return (CompilerErrorType)Wrapper.AerErrGetSeverity(errorCode);
}
}
以下是针对 ExceptionResolver
类的全面解析,结合其设计模式和实现细节:
1. 核心功能
该类是一个 错误码到异常类型的解析器,主要职责包括:
-
动态映射错误码到异常类型:通过反射和XML配置建立映射关系
-
统一异常创建接口:封装异常实例化逻辑
-
多语言错误消息支持:根据文化设置获取本地化错误信息
-
错误分类系统:支持基于类别层次结构的异常匹配
2. 关键设计模式
(1)复合模式(Composite)
-
树形结构存储:
CategoryStorage
构成嵌套的类别树,每个节点可关联异常类型 -
特化处理:
RootCategoryStorage
增加直接错误码映射
(2)工厂模式
-
Resolve()
方法:根据错误码动态创建对应异常实例 -
Activator.CreateInstance
:反射构造异常对象
3. 核心实现分析
(1)静态构造函数(初始化映射)
static ExceptionResolver() {
// 1. 扫描程序集中所有A3200Exception子类
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) {
if (type.IsSubclassOf(typeof(A3200Exception))) {
// 2. 解析CategoryAttribute标记
var attrs = type.GetCustomAttributes<CategoryAttribute>();
foreach (var attr in attrs) {
if (attr.ErrorCode.HasValue) {
rootStorage.ErrorCodeSpecific[attr.ErrorCode.Value] = type;
} else {
// 3. 构建类别树
BuildCategoryTree(rootStorage, attr.Names, type);
}
}
}
}
// 4. 加载XML配置
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(InfoResources.errorCodesInfo);
// 5. 建立最终映射
foreach (FieldInfo field in typeof(ErrorCode).GetFields()) {
ErrorCode code = (ErrorCode)field.GetValue(null);
XmlNode node = xmlDoc.SelectSingleNode($"/error[@name='{field.Name}']");
resolution[code] = DetermineExceptionType(code, node, rootStorage);
extraInfo[code] = node;
}
}
(2)异常解析流程
public static Exception Resolve(Controller controller, ErrorData err, string message) {
ErrorCode code = (ErrorCode)err.Code;
if (!resolution.TryGetValue(code, out Type exceptionType)) {
throw new ArgumentOutOfRangeException("Invalid error code");
}
// 动态构造异常实例
var args = controller != null ?
new object[] { controller, err, extraInfo[code], message } :
new object[] { err, extraInfo[code], message };
return (Exception)Activator.CreateInstance(
exceptionType,
BindingFlags.NonPublic | BindingFlags.Instance,
null, args, null);
}
(3)错误消息本地化
public static string GetErrorMessage(ErrorData errorData, string extendedMsg) {
StringBuilder sb = new StringBuilder(512);
Wrapper.AerNewErrGetMessage(
errorData,
extendedMsg,
sb,
sb.Capacity,
CultureInfo.CurrentUICulture.LCID); // 关键文化参数
return sb.ToString();
}
4. 数据结构设计
存储结构 | 类型 | 用途 |
---|---|---|
resolution | Dictionary<ErrorCode, Type> | 错误码→异常类型直接映射 |
extraInfo | Dictionary<ErrorCode, XmlNode> | 错误码对应的XML配置节点 |
CategoryStorage | 嵌套字典树 | 支持基于类别的异常类型查找 |
5. 使用示例
(1)抛出异常
void HandleError(Controller ctrl, ErrorData err) {
ExceptionResolver.ResolveThrow(ctrl, err);
// 等效于:
// throw ExceptionResolver.Resolve(ctrl, err, null);
}
(2)获取错误消息
string msg = ExceptionResolver.GetErrorMessage(
new ErrorData(ErrorCode.DeviceNotReady),
"Additional details");
(3)自定义异常类型
[Category(ErrorCode.Overheat)]
[Category(ExceptionCategory.Hardware, ExceptionCategory.Temperature)]
public class OverheatException : A3200Exception {
// ...
}
6. 改进建议
-
缓存优化
private static ConcurrentDictionary<ErrorCode, Type> _resolution;
DI 支持
public interface IExceptionResolver {
Exception Resolve(ErrorData err);
}
异步异常
public static async Task ThrowAsync(ErrorData err) {
await Task.Run(() => ResolveThrow(err));
}
性能分析
[Conditional("PERF")]
void LogResolutionTime(ErrorCode code) { ... }
总结
该设计的主要优势:
-
灵活性:通过反射和XML实现动态配置
-
可扩展性:轻松添加新的错误码和异常类型
-
国际化:内置多语言消息支持
-
分层分类:支持从通用到特定的异常匹配
适用于需要精细控制错误处理的工业控制系统,其架构可复用于需要复杂错误映射的场景。