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

如何正确获取Lambda Action的Target并转换为自定义类?

问题分析与解决方案

这个问题的核心是编译器对Lambda表达式捕获变量的自动包装导致的:

当你在foreach里写Outputs.Add((a) => f(3));时,Lambda捕获了循环变量f(一个Action<int>委托),C#编译器会自动生成一个匿名的显示类(就是你调试时看到的DisplayClass),把被捕获的f作为这个类的私有字段存储起来。此时你添加到Outputs里的Action<int>委托,它的Target指向的是这个编译器生成的显示类实例,而不是原来f的Target(你的MyClass实例)。

调试时能看到f字段是因为调试模式下编译器保留了更多调试信息,而发布模式下编译器会优化这些生成的类和字段名(比如变成<f>5__1这类无意义的名字),所以直接访问f字段在非调试环境下会失效。


解决方案1:直接捕获目标实例(推荐)

既然你最终要获取的是f.Target对应的MyClass实例,不如在循环时直接把这个实例提取出来,让Lambda捕获这个实例而非原委托。这样生成的Lambda的Target就是你的MyClass实例,转换时就不会报错了:

List<Action<int>> Outputs = new List<Action<int>>();
List<Action<int>> Inputs; // 假设已填充

foreach(Action<int> f in Inputs)
{
    // 先从原委托中取出MyClass实例
    MyClass target = f.Target as MyClass;
    if(target != null)
    {
        // Lambda直接捕获target,而不是f
        Outputs.Add(a => target.YourMethod(3)); // 替换成你原来f调用的实际方法
    }
}

foreach(Action<int> del in Outputs)
{
    // 现在del.Target就是MyClass实例,可以直接转换
    MyClass mc = (MyClass)del.Target;
}

这种方法完全不依赖编译器的内部实现,是最稳定可靠的方案。

解决方案2:通过反射获取原委托(应急方案)

如果因为某些原因无法修改Lambda的捕获逻辑,只能通过反射来读取编译器生成类里的原委托字段。这种方法依赖编译器的实现细节,虽然当前版本的C#编译器都会生成这样的字段,但未来可能有变化,所以只建议作为临时方案:

foreach(Action<int> del in Outputs)
{
    var displayClass = del.Target;
    if(displayClass == null) continue;

    // 查找显示类中类型为Action<int>的私有实例字段
    var field = displayClass.GetType()
        .GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
        .FirstOrDefault(f => f.FieldType == typeof(Action<int>));

    if(field != null)
    {
        // 获取原委托实例
        var originalAction = (Action<int>)field.GetValue(displayClass);
        // 转换原委托的Target到MyClass
        MyClass mc = originalAction.Target as MyClass;
        if(mc != null)
        {
            // 这里可以使用mc了
        }
    }
}

内容的提问来源于stack exchange,提问作者Pikachu620

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 17:42:28