循环优化或lambda关闭有问题吗?

如何解决循环优化或lambda关闭有问题吗?

| 在下面的方法中,我要发送动作的枚举,并希望返回一组ICommands数组,这些数组将调用“ 0”来包装这些动作(relayCommand是必需的)。 问题是,如果我在for每个(甚至是for循环)内部执行此操作,则会得到始终执行参数中传递的第一个动作的命令。
    public static ICommand[] CreateCommands(IEnumerable<Action> actions)
    {
        List<ICommand> commands = new List<ICommand>();

        Action[] actionArray = actions.ToArray();

        // works
        //commands.Add(new RelayCommand(o => { actionArray[0](); }));  // (_execute = {Method = {Void <CreateCommands>b__0(System.Object)}})
        //commands.Add(new RelayCommand(o => { actionArray[1](); }));  // (_execute = {Method = {Void <CreateCommands>b__1(System.Object)}})

        foreach (var action in actionArray)
        {
            // always add the same _execute member for each RelayCommand (_execute = {Method = {Void <CreateCommands>b__0(System.Object)}})
            commands.Add(new RelayCommand(o => { action(); }));
        }

        return commands.ToArray();
    }
似乎lambda总是在循环内重用,以为它做了相同的事情,但事实并非如此。 我该如何克服这种情况? 我怎样才能强制循环不断威胁新的“ѭ2”? 谢谢! 我按照建议尝试了什么,但没有帮助:
        foreach (var action in actionArray)
        {
            Action<object> executeHandler = o => { action(); };
            commands.Add(new RelayCommand(executeHandler));
        }
似乎对我有用的是:
    class RelayExecuteWrapper
    {
        Action _action;

        public RelayExecuteWrapper(Action action)
        {
            _action = action;
        }

        public void Execute(object o) 
        {
            _action();
        }
    }

/// ...
    foreach (var action in actionArray)
    {
        RelayExecuteWrapper rxw = new RelayExecuteWrapper(action);
        commands.Add(new RelayCommand(rxw.Execute));
    }
RelayCommand代码:
/// <summary>
/// A command whose sole purpose is to 
/// relay its functionality to other
/// objects by invoking delegates. The
/// default return value for the CanExecute
/// method is \'true\'.
/// </summary>
public class RelayCommand : ICommand
{
    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;        

    #endregion // Fields

    #region Constructors

    /// <summary>
    /// Creates a new command that can always execute.
    /// </summary>
    /// <param name=\"execute\">The execution logic.</param>
    public RelayCommand(Action<object> execute)
        : this(execute,null)
    {
    }

    /// <summary>
    /// Creates a new command.
    /// </summary>
    /// <param name=\"execute\">The execution logic.</param>
    /// <param name=\"canExecute\">The execution status logic.</param>
    public RelayCommand(Action<object> execute,Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException(\"execute\");

        _execute = execute;
        _canExecute = canExecute;           
    }

    #endregion // Constructors

    #region ICommand Members

    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    #endregion // ICommand Members
}
    

解决方法

        每周在StackOverflow上报告此问题的次数。问题在于,在循环内部创建的每个新lambda都共享相同的\“ action \”变量。 Lambda不捕获值,而是捕获变量。也就是说,当你说
List<Action> list = new List<Action>();
foreach(int x in Range(0,10))
    list.Add( ()=>{Console.WriteLine(x);} );
list[0]();
当然会打印\“ 10 \”,因为那是x的值。操作是“写x的当前值”,而不是“写创建委托时x返回的值。” 要解决此问题,请创建一个新变量:
List<Action> list = new List<Action>();
foreach(int x in Range(0,10))
{
    int y = x;
    list.Add( ()=>{Console.WriteLine(y);} );
}
list[0]();
由于这个问题非常普遍,我们正在考虑更改下一个C#版本,以便每次通过foreach循环都创建一个新变量。 有关更多详细信息,请参见http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/。 更新:从评论:   每个ICommand都具有相同的methodinfo:
{ Method = {Void <CreateCommands>b__0(System.Object)}}
是的,当然可以。每次方法都相同。我认为您误会了代表创建的含义。这样看。假设您说:
var firstList = new List<Func<int>>() 
{ 
  ()=>10,()=>20 
};
好的,我们有一个返回整数的函数列表。第一个返回10,第二个返回20。 这与以下内容相同:
static int ReturnTen() { return 10; }
static int ReturnTwenty() { return 20; }
...
var firstList = new List<Func<int>>() 
{ ReturnTen,ReturnTwenty };
到目前为止有意义吗?现在,我们添加您的foreach循环:
var secondList = new List<Func<int>>();
foreach(var func in firstList)
    secondList.Add(()=>func());
好那是什么意思这与以下内容完全相同:
class Closure
{
    public Func<int> func;
    public int DoTheThing() { return this.func(); }
}
...
var secondList = new List<Func<int>>();
Closure closure = new Closure();
foreach(var func in firstList)
{
    closure.func = func;
    secondList.Add(closure.DoTheThing);
}
现在清楚这里发生了什么吗?每次循环时,您都不会创建新的闭包,当然也不会创建新的方法。您创建的委托始终指向相同的方法,并且始终指向相同的闭包。 现在,如果您写了
foreach(var loopFunc in firstList)
{
    var func = loopFunc;
    secondList.Add(func);
}
那么我们将生成的代码将是
foreach(var loopFunc in firstList)
{
    var closure = new Closure();
    closure.func = loopFunc;
    secondList.Add(closure.DoTheThing);
}
现在列表中的每个新函数都具有相同的methodinfo-仍为DoTheThing-但闭包不同。 现在您为什么看到结果有意义吗? 您可能还需要阅读: Lambda在C#中创建的委托的寿命是多少? 另一个更新:从已编辑的问题:   我按照建议尝试了什么,但没有帮助:
    foreach (var action in actionArray)         
    {
         Action<object> executeHandler = o => { action(); };
         commands.Add(new RelayCommand(executeHandler));         } 
    }
当然那没有帮助。这与以前完全一样。问题在于lambda是在单个变量“ action”上而不是在每个action值上封闭的。在创建lambda的位置四处移动显然不能解决该问题。您要做的是创建一个新变量。第二种解决方案是通过创建引用类型的字段来分配新变量来实现的。您不需要明确地执行此操作;正如我上面提到的,如果在循环主体内部创建一个新的变量,编译器将为您执行此操作。 解决该问题的正确且简短的方法是
    foreach (var action in actionArray)         
    {
         Action<object> copy = action;
         commands.Add(new RelayCommand(x=>{copy();}));
    }
这样,您每次在循环中都会创建一个新变量,因此循环中的每个lambda都会覆盖一个不同的变量。 每个委托将具有相同的methodinfo,但闭包不同。   我不确定这些闭包和lambda 您正在程序中进行高阶函数编程。如果希望有机会正确地执行“这些闭包和lambdas”,则最好了解。没有时间像现在这样。     ,        我只是做了一个实际的例子,说明您正在尝试做什么:http://ideone.com/hNcGx
    interface ICommand
    {
        void Print();
    }

    class CommandA : ICommand
    {
        public void Print() { Console.WriteLine(\"A\"); }
    }

    class CommandB : ICommand
    {
        public void Print() { Console.WriteLine(\"B\"); }
    }

    public static void Main()
    {
        var actions = new List<Action>();
        foreach (var command in new ICommand[]{
                    new CommandA(),new CommandB(),new CommandB()})
        {
            var commandcopy = command;
            actions.Add(() => commandcopy.Print());
        }

        foreach (var action in actions)
            action();
    }
输出:
A
B
B
这有帮助吗?     ,        在循环范围内局部引用ѭ19。
foreach (var action in actionArray)
{ 
   var myAction = action;
   // always add the same _execute member for each RelayCommand (_execute = {Method = {Void <CreateCommands>b__0(System.Object)}})
   commands.Add(new RelayCommand(o => { action(); }));
}
    ,        您只会使用actionArray数组中的第一项。 即
commands.Add(new RelayCommand(o => { actionArray[0](); }));
您需要遍历动作集合。 例如
public static ICommand[] CreateCommands(IEnumerable<Action> actions)
{
  commands = actions.Select(o => new RelayCommand(o)).ToArray();
}
代码是徒手写的,因此可能会有一些错别字,但应该为您指出正确的想法。     

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-