我已经创建了一个最小的例子来复制一个奇怪的Vala行为,我不明白并且想要解释。
类Test
的构造函数使用Func
并使用它来初始化其类成员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
实例化Test.FUNC_0
对象,并进行一些测试:
public static void main()
{
assert( Test.FUNC_0 != null ); // first assert
var t = new Test( Test.FUNC_0 );
assert( t.f != null ); // second assert
}
现在对此有什么奇怪的?
Test.FUNC_0
是 null
。 那怎么样?!valac
向我发出警告,指出"不支持复制代理" ,但在第10行,即this.f = f
分配,所以这警告不考虑Test.FUNC_0
字段。assert
并将Test.FUNC_0
的{{1}}参数替换为new Test
,则第二个() => { return 0; }
会通过。那么第10行assert
的错误是什么? 第10行中的闭包是复制还是不是?this.f = f
中作为类成员引用?我很高兴看到这个解释。 Test
版本为0.28.1。
答案 0 :(得分:3)
您的问题实际上与委托无关,也与单独拥有的实例无关。 Vala中的所有非基本类型都是拥有或非拥有的。某些类(包括派生自GLib.Object
的类)可以拥有多个所有者。当需要该类的副本时,其目标类的引用计数会递增。其他类(包括string
)和结构只能有一个所有者,但带有一个复制函数,允许生成该类的副本。代表和某些类(如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();