d只赋值一次循环变量
原文
我正在用CSFML
的D绑定
,并且创建了自己的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`参数的名称
这与HSTeoh
和Tsbockman
的代码
相同:通过创建按参数取结构
的函数字面
,来在当前在foreach
的特定迭代
中,创建值的副本
.这是有效
的,因为结构
是值类型
,因此传递它给函数
会创建副本.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现