System.NullReferenceException: 未将对象引用设置到对象的实例 表示在代码中尝试访问一个未初始化的对象(即 null 对象的成员)
System.NullReferenceException: 未将对象引用设置到对象的实例 表示在代码中尝试访问一个未初始化的对象(即 null 对象的成员)。在提供的代码中,错误提示 outputParameterMap 为 null,说明在以下代码块中,outputParameterMap 未被正确赋值或初始化,导致后续访问其成员(如 ContainsKey 或键值访问)时抛出异常:
csharp
outputParameterMap = JsonNewtonsoft.FromJSON<Dictionary<string, object>>(OutPut);
m_Value.Error = errCode;
// 以下代码访问 outputParameterMap 的成员
if (outputParameterMap.ContainsKey("HightLimitSensor"))
{
m_Value.ResultMap["HighLimitSensor"] = (bool)outputParameterMap["HightLimitSensor"];
}
可能导致 outputParameterMap 为 null 的原因包括:
-
OutPut 为空或无效:OutPut 字符串可能为空、格式不正确或无法被 JsonNewtonsoft.FromJSON 正确解析为 Dictionary<string, object>。
-
PLCAux.LabVIEWExports.DoControl 返回异常数据:DoControl 方法可能返回了无效的 OutPut 值(例如空字符串、格式错误的 JSON 或 null)。
-
JSON 解析失败:JsonNewtonsoft.FromJSON 方法可能无法将 OutPut 解析为有效的 Dictionary<string, object>,从而返回 null。
优化建议
以下是针对此问题的优化方案,旨在防止 NullReferenceException,并提高代码的鲁棒性和可维护性:
1. 检查 OutPut 是否有效
在调用 JsonNewtonsoft.FromJSON 之前,检查 OutPut 是否为 null 或空字符串,以避免解析失败。
csharp
if (string.IsNullOrEmpty(OutPut))
{
m_Value.Error = "Output data is empty or null";
if (RuntimeConfiguration.IsPrintLog)
{
string viStr = string.Format("COM={0} Press QUERY Error: Empty or null OutPut, InPut={1}", HWSelect, InPut);
m_FileLogger.Info(viStr);
}
return 4; // 返回特定错误码表示输出数据无效
}
2. 检查 JSON 解析结果
在调用 JsonNewtonsoft.FromJSON 后,检查 outputParameterMap 是否为 null。
csharp
outputParameterMap = JsonNewtonsoft.FromJSON<Dictionary<string, object>>(OutPut);
if (outputParameterMap == null)
{
m_Value.Error = "Failed to parse output JSON";
if (RuntimeConfiguration.IsPrintLog)
{
string viStr = string.Format("COM={0} Press QUERY Error: Invalid JSON format, InPut={1}, OutPut={2}", HWSelect, InPut, OutPut);
m_FileLogger.Info(viStr);
}
return 5; // 返回特定错误码表示 JSON 解析失败
}
3. 使用 Try-Catch 捕获 JSON 解析异常
JsonNewtonsoft.FromJSON 可能因格式错误抛出异常,建议使用 try-catch 块来捕获潜在的 JsonException。
csharp
try
{
outputParameterMap = JsonNewtonsoft.FromJSON<Dictionary<string, object>>(OutPut);
if (outputParameterMap == null)
{
m_Value.Error = "Failed to parse output JSON";
return 5;
}
}
catch (Exception ex)
{
m_Value.Error = $"JSON parsing error: {ex.Message}";
if (RuntimeConfiguration.IsPrintLog)
{
string viStr = string.Format("COM={0} Press QUERY Error: JSON parsing failed, InPut={1}, OutPut={2}, Exception={3}", HWSelect, InPut, OutPut, ex.Message);
m_FileLogger.Info(viStr);
}
return 6; // 返回特定错误码表示 JSON 解析异常
}
4. 优化 ContainsKey 检查逻辑
为了减少代码重复,可以将 outputParameterMap 的键值检查和赋值逻辑封装到一个辅助方法中,提高代码可读性和可维护性。
csharp
private void MapOutputToResult(Dictionary<string, object> outputParameterMap, Dictionary<string, object> resultMap)
{
var mappings = new Dictionary<string, string>
{
{ "HightLimitSensor", "HighLimitSensor" },
{ "Low LimitSensor", "LowLimitSensor" },
{ "HomeSensor", "HomeSensor" },
// ... 其他映射
{ "DUT1", "DUT3" },
{ "DUT2", "DUT1" },
// ... 其他 DUT 映射
};
var intKeys = new[] { "ReadyStatus", "LoadStatus", "UnLoadStatus", "ResetStatus", "StopStatus", "HomeStatus", "DoorStatus" };
var doubleKeys = new[] { "CurrentPosition", "CurrentSpeed" };
foreach (var mapping in mappings)
{
if (outputParameterMap.ContainsKey(mapping.Key))
{
resultMap[mapping.Value] = (bool)outputParameterMap[mapping.Key];
}
}
foreach (var key in intKeys)
{
if (outputParameterMap.ContainsKey(key))
{
resultMap[key] = Convert.ToInt32(outputParameterMap[key]);
}
}
foreach (var key in doubleKeys)
{
if (outputParameterMap.ContainsKey(key))
{
resultMap[key] = Convert.ToDouble(outputParameterMap[key].ToString());
}
}
}
调用方式:
csharp
MapOutputToResult(outputParameterMap, m_Value.ResultMap);
5. 添加日志记录
在关键步骤添加更详细的日志,记录 InPut 和 OutPut 的内容,便于调试和问题定位。
csharp
if (RuntimeConfiguration.IsPrintLog)
{
string viStr = string.Format("COM={0} Press QUERY InPut={1}, OutPut={2}, ErrorCode={3}", HWSelect, InPut, OutPut, errorCode);
m_FileLogger.Info(viStr);
}
6. 修正 DUT 映射错误
代码中 DUT 的映射存在混乱(例如 DUT1 映射到 DUT3,DUT2 映射到 DUT1 等)。建议明确映射规则,并确保一致性。例如,如果需要重新编号 DUT,可以在映射表中明确定义:
csharp
var dutMappings = new Dictionary<string, string>
{
{ "DUT1", "DUT3" },
{ "DUT2", "DUT1" },
{ "DUT3", "DUT4" },
{ "DUT4", "DUT2" },
// ... 其他 DUT 映射
};
7. 返回值规范化
定义清晰的返回值代码,记录每种错误情况。例如:
-
0: 成功
-
1: 参数缺失
-
2: 参数类型错误
-
3: 输出数据为空
-
4: JSON 解析失败
-
5: 其他异常
在方法开始处定义错误码常量:
csharp
const int SUCCESS = 0;
const int MISSING_PARAMETER = 1;
const int INVALID_PARAMETER_TYPE = 2;
const int EMPTY_OUTPUT = 3;
const int JSON_PARSE_FAILED = 4;
const int GENERAL_ERROR = 5;
8. 完整优化代码示例
以下是将上述建议整合后的代码:
csharp
case "QUERY":
{
string HWSelect = HardwareInfo.HardwareName;
string Path = "Query Module";
string errCode = "0000";
int errorCode = 0;
Dictionary<string, object> inputParameterMap = new Dictionary<string, object>();
Dictionary<string, object> outputParameterMap;
// 检查参数
if (!ins.ParameterMap.ContainsKey("SectionNo"))
{
m_Value.Error = "SectionNo Error";
return MISSING_PARAMETER;
}
if (!int.TryParse(ins.ParameterMap["SectionNo"], out int SectionNo))
{
m_Value.Error = "SectionNo Type Error";
return INVALID_PARAMETER_TYPE;
}
// 准备输入参数
inputParameterMap.Add("Path", Path);
inputParameterMap.Add("SectionNo", SectionNo);
inputParameterMap.Add("HWSelect", HWSelect.Split(',')[0]);
InPut = JsonNewtonsoft.ToJSON(inputParameterMap);
// 调用 DoControl
errorCode = PLCAux.LabVIEWExports.DoControl(InPut, out OutPut);
// 检查输出
if (string.IsNullOrEmpty(OutPut))
{
m_Value.Error = "Output data is empty or null";
if (RuntimeConfiguration.IsPrintLog)
{
string viStr = string.Format("COM={0} Press QUERY Error: Empty or null OutPut, InPut={1}", HWSelect, InPut);
m_FileLogger.Info(viStr);
}
return EMPTY_OUTPUT;
}
// 解析 JSON
try
{
outputParameterMap = JsonNewtonsoft.FromJSON<Dictionary<string, object>>(OutPut);
if (outputParameterMap == null)
{
m_Value.Error = "Failed to parse output JSON";
if (RuntimeConfiguration.IsPrintLog)
{
string viStr = string.Format("COM={0} Press QUERY Error: Invalid JSON format, InPut={1}, OutPut={2}", HWSelect, InPut, OutPut);
m_FileLogger.Info(viStr);
}
return JSON_PARSE_FAILED;
}
}
catch (Exception ex)
{
m_Value.Error = $"JSON parsing error: {ex.Message}";
if (RuntimeConfiguration.IsPrintLog)
{
string viStr = string.Format("COM={0} Press QUERY Error: JSON parsing failed, InPut={1}, OutPut={2}, Exception={3}", HWSelect, InPut, OutPut, ex.Message);
m_FileLogger.Info(viStr);
}
return JSON_PARSE_FAILED;
}
// 设置错误码
errCode = errorCode == 0 ? "0000" : errorCode.ToString();
m_Value.Error = errCode;
// 映射输出到 ResultMap
MapOutputToResult(outputParameterMap, m_Value.ResultMap);
// 记录日志
if (RuntimeConfiguration.IsPrintLog && errorCode != 0)
{
string viStr = string.Format("COM={0} Press QUERY ErrorCode={1}, InPut={2}, OutPut={3}", HWSelect, errorCode, InPut, OutPut);
m_FileLogger.Info(viStr);
}
return SUCCESS;
}
break;
优化效果
-
防止 NullReferenceException:通过检查 OutPut 和 outputParameterMap 是否为空,避免访问 null 对象。
-
提高鲁棒性:使用 try-catch 和错误码机制,处理 JSON 解析失败等异常情况。
-
代码简洁性:通过封装映射逻辑,减少重复代码,提高可维护性。
-
调试友好:添加详细日志,便于定位问题。
-
错误处理规范化:定义明确的错误码,便于调用者处理不同类型的错误。
注意事项
-
验证 OutPut 格式:确保 PLCAux.LabVIEWExports.DoControl 方法返回的 OutPut 是有效的 JSON 格式。可以通过调试或日志记录 OutPut 的内容来确认。
-
DUT 映射一致性:检查 DUT 键的映射是否符合实际需求,避免逻辑错误。
-
性能优化:如果 outputParameterMap 包含大量键值,考虑使用批量处理或优化映射逻辑以提高性能。
-
日志配置:确保 RuntimeConfiguration.IsPrintLog 和 m_FileLogger 已正确配置,避免日志记录失败。
如果有进一步的问题或需要更详细的分析,请提供更多上下文(例如 PLCAux.LabVIEWExports.DoControl 的实现细节或 OutPut 的示例内容)。