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 的原因包括:

  1. OutPut 为空或无效:OutPut 字符串可能为空、格式不正确或无法被 JsonNewtonsoft.FromJSON 正确解析为 Dictionary<string, object>。

  2. PLCAux.LabVIEWExports.DoControl 返回异常数据:DoControl 方法可能返回了无效的 OutPut 值(例如空字符串、格式错误的 JSON 或 null)。

  3. 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;

优化效果

  1. 防止 NullReferenceException:通过检查 OutPut 和 outputParameterMap 是否为空,避免访问 null 对象。

  2. 提高鲁棒性:使用 try-catch 和错误码机制,处理 JSON 解析失败等异常情况。

  3. 代码简洁性:通过封装映射逻辑,减少重复代码,提高可维护性。

  4. 调试友好:添加详细日志,便于定位问题。

  5. 错误处理规范化:定义明确的错误码,便于调用者处理不同类型的错误。


注意事项

  1. 验证 OutPut 格式:确保 PLCAux.LabVIEWExports.DoControl 方法返回的 OutPut 是有效的 JSON 格式。可以通过调试或日志记录 OutPut 的内容来确认。

  2. DUT 映射一致性:检查 DUT 键的映射是否符合实际需求,避免逻辑错误。

  3. 性能优化:如果 outputParameterMap 包含大量键值,考虑使用批量处理或优化映射逻辑以提高性能。

  4. 日志配置:确保 RuntimeConfiguration.IsPrintLog 和 m_FileLogger 已正确配置,避免日志记录失败。

如果有进一步的问题或需要更详细的分析,请提供更多上下文(例如 PLCAux.LabVIEWExports.DoControl 的实现细节或 OutPut 的示例内容)。