You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C# IL.Emit动态将DateTimeOffset?转换为DateTime?时UTC值未被正确使用的问题

C# IL.Emit动态将DateTimeOffset?转换为DateTime?时UTC值未被正确使用的问题

我帮你定位到了问题的核心所在,你的IL指令在处理Nullable<DateTimeOffset>的属性调用时存在错误,导致没有正确获取到UtcDateTime值,最终转换结果不符合预期。

问题根源分析

你错误地使用了OpCodes.Ldarga_S(加载参数的内存地址)来调用Nullable<T>的HasValue和Value属性的get方法,但这两个方法是实例方法,需要传入Nullable<T>的实例本身,而非指针地址。这种错误会导致栈操作异常,或者无法正确获取到DateTimeOffset的实际值,进而UtcDateTime的转换逻辑没有被正确执行。

另外,你直接硬编码从typeof(DateTimeOffset?)获取属性方法,虽然当前场景能工作,但不够通用,最好从输入类型动态获取这些方法,避免耦合特定类型。

修正后的完整代码

下面是修复后的GenerateConverter方法,同时优化了方法获取逻辑和IL指令的正确性:

using System;
using System.Reflection;
using System.Reflection.Emit;

public class Program
{
    public static Func<TInputValue, TOutputValue> GenerateConverter<TInputValue, TOutputValue>()
    {
        var inputType = typeof(TInputValue);
        var outputType = typeof(TOutputValue);

        // 创建动态方法
        var method = new DynamicMethod(
            "ConvertDateTimeOffsetToUtcDateTime",
            outputType,
            new[] { inputType });

        var inputIsNullable = inputType.IsGenericType && inputType.GetGenericTypeDefinition() == typeof(Nullable<>);
        var outputIsNullable = outputType.IsGenericType && outputType.GetGenericTypeDefinition() == typeof(Nullable<>);

        var il = method.GetILGenerator();

        // 动态获取所需的方法和属性
        MethodInfo hasValueMethod = null;
        MethodInfo getValueMethod = null;
        var utctimeMethod = typeof(DateTimeOffset).GetProperty("UtcDateTime").GetGetMethod();

        if (inputIsNullable)
        {
            // 从输入的可空类型动态获取HasValue和Value的get方法
            hasValueMethod = inputType.GetProperty("HasValue").GetGetMethod();
            getValueMethod = inputType.GetProperty("Value").GetGetMethod();
        }

        Label returnNullLabel = il.DefineLabel();
        Label continueLabel = il.DefineLabel();

        // 处理可空输入的空值判断逻辑
        if (inputIsNullable)
        {
            // 加载输入参数实例(DateTimeOffset?)
            il.Emit(OpCodes.Ldarg_0);
            // 调用HasValue判断是否有值
            il.Emit(OpCodes.Call, hasValueMethod);
            il.Emit(OpCodes.Brfalse_S, returnNullLabel);

            // 再次加载输入参数实例,获取实际的DateTimeOffset值
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Call, getValueMethod);
        }
        else
        {
            // 加载非可空的输入实例
            il.Emit(OpCodes.Ldarg_0);
        }

        // 获取UTC DateTime值
        il.Emit(OpCodes.Call, utctimeMethod);

        // 包装为可空输出类型
        if (outputIsNullable)
        {
            var ctor = outputType.GetConstructor(new[] { typeof(DateTime) });
            il.Emit(OpCodes.Newobj, ctor);
        }

        il.Emit(OpCodes.Br_S, continueLabel);

        // 空值分支处理
        il.MarkLabel(returnNullLabel);
        if (outputIsNullable)
        {
            il.Emit(OpCodes.Ldnull);
        }
        else
        {
            // 非可空输出时,输入为空则抛出异常
            il.Emit(OpCodes.Newobj, typeof(InvalidOperationException).GetConstructor(Type.EmptyTypes));
            il.Emit(OpCodes.Throw);
        }

        il.MarkLabel(continueLabel);
        il.Emit(OpCodes.Ret);

        // 创建委托并返回
        return (Func<TInputValue, TOutputValue>)method.CreateDelegate(typeof(Func<TInputValue, TOutputValue>));
    }

    public static void Main()
    {
        var converter = GenerateConverter<DateTimeOffset?, DateTime?>();

        // 测试有值的情况(东8区时间转UTC)
        DateTimeOffset? inputWithValue = new DateTimeOffset(2024, 5, 20, 10, 0, 0, TimeSpan.FromHours(8));
        DateTime? outputWithValue = converter(inputWithValue);
        Console.WriteLine($"Input: {inputWithValue}, Output UTC: {outputWithValue}");
        // 预期输出:Input: 2024/5/20 10:00:00 +08:00, Output UTC: 2024/5/20 2:00:00

        // 测试空值情况
        DateTimeOffset? inputNull = null;
        DateTime? outputNull = converter(inputNull);
        Console.WriteLine($"Input is null, Output is null: {outputNull == null}");
        // 预期输出:Input is null, Output is null: True
    }
}

关键修正点总结

  1. 修正Nullable属性调用方式:使用OpCodes.Ldarg_0加载Nullable<T>实例,而非Ldarga_S加载地址,确保实例方法能正确接收参数。
  2. 动态获取属性方法:从输入的可空类型动态获取HasValue和Value的get方法,避免硬编码依赖特定类型。
  3. 保证栈平衡:严格控制IL指令的栈操作顺序,确保每个指令执行后栈的状态符合CLR要求,避免运行时异常。

备注:内容来源于stack exchange,提问作者Max_dev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.16 02:58:02