混淆委托和闭包在 Vala 中的行为

Confused how Delegates and Closures behave in Vala

我创建了一个最小示例,它重现了一个奇怪的 Vala 行为,我不明白,希望得到解释。

class Test 的构造函数接受一个 Func 并使用它来初始化它的 class 成员 f:

public class Test
{
    public delegate int Func();
    public static Func FUNC_0 = () => { return 0; };

    public Func f;

    public Test( Func f )
    {
        this.f = f;   // line 10
    }
}

我使用 Test.FUNC_0 中定义的 Func 实例化了一个 Test 对象,并做了一些测试:

public static void main()
{
    assert( Test.FUNC_0 != null );   // first assert
    var t = new Test( Test.FUNC_0 );
    assert( t.f != null );           // second assert
}

现在这有什么奇怪的?

我真的很高兴看到这个解释。 valac 版本是 0.28.1.

您的问题实际上与委托无关,而与单独拥有的实例有关。 Vala 中的所有非原始类型要么是拥有的,要么是无主的。某些 classes(包括派生自 GLib.Object 的)可以有多个所有者。当需要 class 的副本时,目标 class 上的引用计数会增加。其他 classes(包括 string)和结构只能有一个所有者,但带有一个复制函数,允许生成 class 的副本。代表和某些 classes(如 FileStream)也只有一个所有者,但不能被复制。

无法复制委托的原因是委托包含三部分信息:回调函数、一些上下文数据,也许还有上下文数据的析构函数。没有复制功能

由于默认情况下参数是无主的,this.f = f 正在尝试将它不拥有的委托复制到它拥有的引用中。这将是内存不安全的,因为它会在对象的生命周期之后保留引用,或者析构函数可能会被调用两次。

您有两个选择:

public class Test
{
    public delegate int Func();
    public static Func FUNC_0 = () => { return 0; };

    public Func f;

    public Test( owned Func f )
    {
        this.f = (owned) f;   // you are just moving the delegate, not copying it.
    }
}

或者:

public class Test
{
    public delegate int Func();
    public static Func FUNC_0 = () => { return 0; };

    public unowned Func f;

    public Test( Func f )
    {
        this.f = f;   // You are copying an unowned reference to another
        // unowned reference, which is fine. Memory management is now your
        // job and not Vala's.
    }
}

第二个有点危险。例如,以下代码将编译并破坏内存:

Test? t = null;
if (true) {
  int x = 5;
  Func f = () => { return x; };
  t = new Test(f);
}
t.f();