【问题标题】:How to capture an int value in a closure?如何在闭包中捕获 int 值?
【发布时间】:2021-07-03 01:53:09
【问题描述】:

我有一个应用程序,其中包含任意数量的元素,这些元素都调用一个需要接受参数的函数,在创建时定义。这是一个简化的示例,但在这里我希望制作 3 个打印 0、1、2 的按钮,但只制作 3 个打印 3 的按钮。

    var application_window = new Gtk.ApplicationWindow (this);
    var grid = new Gtk.Grid ();

    for (int i = 0; i < 3; i++) {
        var button = new Gtk.Button() {expand=true};
        button.clicked.connect (() => {
                print(i.to_string());
        });

        grid.add(button);
    }

    application_window.add(grid);
    application_window.show_all ();

如何将我的应用改为打印 123?

【问题讨论】:

    标签: vala


    【解决方案1】:

    这是我的基本代码:

    public class MyApplication : Gtk.Application {
        public MyApplication () {
            Object(application_id: "testing.my.application",
                flags : ApplicationFlags.FLAGS_NONE);
        }
    
        protected override void activate () {
            var application_window = new Gtk.ApplicationWindow (this);
            var grid = new Gtk.Grid ();
    
            for (int i = 0; i < 3; i++) {
                var button = new Gtk.Button() {expand=true};
                button.clicked.connect (() => {
                    print(i.to_string());
                });
    
                grid.add(button);
            }
    
            application_window.add(grid);
            application_window.show_all ();
        }
    
        public static int main (string[] args) {
            MyApplication app = new MyApplication ();
            return app.run (args);
        }
    }
    

    如果你这样执行它,你会得到333 作为标准输出。

    问题出在捕获代码中:

    for (int i = 0; i < 3; i++) {
        var button = new Gtk.Button() {expand=true};
        button.clicked.connect (() => {
            print(i.to_string());
        });
    

    闭包按位置捕获变量 i。这意味着当您在创建闭包后更改 i 变量时,更改也将在闭包中可见。

    其他编程语言(例如 C++)有明确的捕获列表来避免这个问题。

    快速而肮脏的解决方案是在循环范围内使用局部变量:

    for (int i = 0; i < 3; i++) {
        var captured_i = i;
        var button = new Gtk.Button() {expand=true};
        button.clicked.connect (() => {
            print(captured_i.to_string());
        });
    

    这会按预期打印:012

    更好的解决方案是使用将闭包作为委托返回的函数。我刚刚尝试过,但由于某种原因它不起作用:

    public class MyApplication : Gtk.Application {
        public MyApplication () {
            Object(application_id: "testing.my.application",
                flags : ApplicationFlags.FLAGS_NONE);
        }
    
        delegate void ButtonClick();
    
        private ButtonClick make_print_event (int i) {
            return () => print (i.to_string());
        }    
    
        protected override void activate () {
            var application_window = new Gtk.ApplicationWindow (this);
            var grid = new Gtk.Grid ();
    
            for (int i = 0; i < 3; i++) {
                var button = new Gtk.Button() { expand=true };
                var print_event = make_print_event (i);
                button.clicked.connect (print_event);
                grid.add(button);
            }
    
            application_window.add (grid);
            application_window.show_all ();
        }
    
        public static int main (string[] args) {
            MyApplication app = new MyApplication ();
            return app.run (args);
        }
    }
    

    编译器 (valac-0.52) 警告:

    three_buttons.vala:20.37-20.47: warning: copying delegates is not supported
    three_buttons.vala:20.37-20.47: warning: Connecting delegates to signals is experimental
                button.clicked.connect (print_event);
                                        ^^^^^^^^^^^
    

    【讨论】:

      猜你喜欢
      • 2021-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-16
      相关资源
      最近更新 更多