d只赋值一次循环变量

原文
我正在用CSFMLD绑定,并且创建了自己的UI库,来将元素绘画到屏幕上.
其中一个我创建的类是,包含用户点击按钮时调用的叫点击按钮时(onButtonClick)的函数的按钮类.
目前,一切运行良好.
我想添加几个并排按钮,来表示列表中元素,并为列表中指定元素们每个分配唯一的λ式,这是我当前的代码:

foreach (BoardSize boardSize; arr) {
    Button button = new Button();
    button.text = format("%sx%s", boardSize[0], boardSize[1]);
    button.onButtonClick = {
        eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize);
    };
    button.onButtonClick();
    _boardSizeRow.addChild(button);
}

运行这段代码,原以为一切都会正常工作,但可惜,在运行代码时,点击每个按钮,只返回最大的最后迭代板大小(boardSize)值.
我不确定为什么会这样?
更新:唯一解决方法:似乎是用静每一数组:

Button[3] b;

static foreach (indx, BoardSize boardSize; arr) {
    b[indx] = new Button();
    b[indx].text = format("%sx%s", boardSize[0], boardSize[1]);
    b[indx].onButtonClick = {
        eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize);
    };
    _boardSizeRow.addChild(b[indx]);
}

还有其他方法可解决该恼人问题吗?

问题有两个方面:
1,D中闭包引用环境.
2,在单个函数调用中的每个循环迭代中,D(我认为是错误的)认为循环局部变量都有相同标识.

所以,在你的事件处理器中,板大小(boardSize),是对单个变量的引用,在循环每次迭代中,都会覆盖该变量值.因为,在循环终止前不会调用事件处理器,所以只看见函数调用循环的最后一次迭代所设置的值.

至少有两种可能解决方法:
1,使用来明确按值而不是按引用捕捉板大小:

static struct ClickHandler {
 //如果`eventHandler`不是全局变量,则为它另外添加一个字段
    BoardSize iteration_boardSize;
    this(BoardSize iteration_boardSize) {
        this.iteration_boardSize = iteration_boardSize;
    }

    void opCall() {
        eventHandler.settingsWindow_onBoardSizeButtonClick(iteration_boardSize);
    }
}

foreach (BoardSize loop_boardSize; arr) {
    Button button = new Button();
    button.text = format("%sx%s", loop_boardSize[0], loop_boardSize[1]);
    button.onButtonClick = &(new ClickHandler(loop_boardSize)).opCall;
    button.onButtonClick();
    _boardSizeRow.addChild(button);
}

2,在循环每次迭代上,用板大小(boardSize)调用嵌套函数,来用唯一标识创建板大小副本:

foreach (BoardSize loop_boardSize; arr) {
    Button button = new Button();
    button.text = format("%sx%s", loop_boardSize[0], loop_boardSize[1]);
    button.onButtonClick = (BoardSize iteration_boardSize) {
        return {
            eventHandler.settingsWindow_onBoardSizeButtonClick(iteration_boardSize);
        };
    }(loop_boardSize);
    button.onButtonClick();
    _boardSizeRow.addChild(button);
}

启用优化时,这两种方法应编译成大致相同的运行时代码.前者更明确,而后者更简洁.

静每一语义上等价于复制和粘贴循环体arr.length次数,用boardSize替换arr[indx],及indx替换indx字面值.

运行时(非静态)循环不同,事件处理器闭包不抓板大小或indx的索引,因为运行时它们并不存在,因此无法引用.
在编译时创建arr.length个不同的都用适当indx字面替换的事件处理器函数.

所以,虽然只要编译时已知arr.length,它确实有效,但除非arr.length非常小,它膨胀了大量不必要代码.

啊,我想你遇见了闭包错误.试试以下代码:

foreach (BoardSize boardSize; arr) (){//注意括号
     Button button = new Button();
     button.text = format("%sx%s", boardSize[0], boardSize[1]);
     button.onButtonClick = {

 eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize);
     };
     button.onButtonClick();
     _boardSizeRow.addChild(button);
 }() // 注意括号

这不是你的错,这是dmd的一种"优化",以便用户发现在每一中抓发出的每个变量,将分配新的堆闭包,不会感到惊讶.
祝你阅读愉快:问题.

这是经典的D陷阱:只分配一次循环变量,闭包抓了循环变量所在的唯一位置,因此每次循环迭代的每个闭包,在以后运行时都看到相同的循环索引的最后值.你要这样:

foreach (BoardSize boardSize; arr) {
    Button button = new Button();
    button.text = format("%sx%s", boardSize[0], boardSize[1]);
    BoardSize size = boardSize; // 强制分开捕捉
    button.onButtonClick = {
        eventHandler.settingsWindow_onBoardSizeButtonClick(size);
    };
    button.onButtonClick();
    _boardSizeRow.addChild(button);
}

这可说是语言错误(我想不到理智用例),但没人成功说服沃尔特.

你的抓变量代码,似乎不管用
啊,显然循环体内部局部变量也会受到同样古怪行为的影响.>:-(以下是一个可行变通方法.

foreach (BoardSize boardSize; arr) {
    Button button = new Button();
    button.text = format("%sx%s", boardSize[0], boardSize[1]);
    void wowThisIsDumb(BoardSize size) {
        button.onButtonClick = {
        eventHandler.settingsWindow_onBoardSizeButtonClick(size);
        };
    }
    wowThisIsDumb(boardSize);
    button.onButtonClick();
    _boardSizeRow.addChild(button);
}

需要嵌套函数(或内联λ)来强制为分配动态环境.
真是尴尬.为什么还没有解决该问题?😦

好吧,不好意思写错了代码,正确的代码是:

 foreach (BoardSize boardSizeIter; arr) (Boardsize boardSize){ // 注意括号,我稍微更改了变量名
     Button button = new Button();
     button.text = format("%sx%s", boardSize[0], boardSize[1]);
     button.onButtonClick = {

 eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize);
     };
     button.onButtonClick();
     _boardSizeRow.addChild(button);
 }(boardSizeIter) 
 //注意括号,我首先更改了`foreach`参数的名称

这与HSTeohTsbockman代码相同:通过创建按参数取结构函数字面,来在当前在foreach特定迭代中,创建值的副本.这是有效的,因为结构值类型,因此传递它给函数会创建副本.

posted @   zjh6  阅读(72)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示