在Matlab类中,同时声明Dependent(计算而非存储)和Observable的属性在语法上似乎是正确的。考虑代码
properties (Access = private)
instanceOfAnotherClass
end
properties (SetAccess = private, Dependent, SetObservable)
propertyTwo
end
methods
function val = get.propertyTwo(this)
val = this.instanceOfAnotherClass.propertyOne;
end
end
这是否如预期的那样有效?也就是说,如果存储在instanceOfAnotherClass
中的对象的属性propertyOne
发生了更改,是否存在由propertyTwo
触发的属性更改事件?注意,propertyOne
不是可观测的。
编辑:它不起作用(正如我所预料的那样)未触发PostSet事件。那么我该如何应对这种情况呢?有没有更好的解决方案可以将propertyTwo
创建为非依赖,并在每次"propertyOne"更改时将其设置为与"propertyOne'"相同的值?
第2版:作为对阿姆罗修改答案的回应,我将解释更为复杂的情况。考虑这两类:
classdef AClass < handle
properties
a
end
end
classdef BClass < handle
properties (Access = private)
aClassInst
end
properties (Dependent, SetObservable, SetAccess = private)
b
end
methods
function this = BClass(aClass)
this.aClassInst = aClass;
end
function val = get.b(this)
val = this.aClassInst.a;
end
end
end
使用所有这些代码的类不应该访问AClass
。它只与BClass
的实例交互,并希望侦听属性b
的更改。然而,如果我使AClass
的性质a
是可观察的,这不会解决我的问题,是吗?"PostSet"事件不会传播到属性b
,是吗?
它在语法上可能是正确的,但侦听器回调永远不会执行。示例:
classdef MyClass < handle
properties (Access = public)
a
end
properties (SetAccess = private, Dependent, SetObservable)
b
end
methods
function val = get.b(this)
val = this.a;
end
end
end
现在试试:
c = MyClass();
lh = addlistener(c, 'b', 'PostSet',@(o,e)disp(e.EventName));
c.a = 1;
disp(c.b)
正如您所看到的,"PostSet"回调从未执行过。
编辑
在我看来,SetObservable
实际上应该设置在a
上,而不是b
上。这是因为b
是只读的,只有当a
更改时才能更改。现在PostSet
事件将通知我们这两个属性都已更改。
使用我上面使用的相同示例,简单地将SetObservable
从b
移动到a
。当然,现在你听的事件是:
lh = addlistener(c, 'a', 'PostSet',@(o,e)disp(e.EventName));
编辑#2
很抱歉,我没有注意到你有composition(BClass有一个AClass实例作为私有属性)。
考虑这个可能的解决方案:
A等级.m
classdef AClass < handle
properties (SetObservable)
a %# observable property
end
end
B等级.m
classdef BClass < handle
properties (Access = private)
aClassInst %# instance of AClass
lh %# event listener on aClassInst.a
end
properties (Dependent, SetAccess = private)
b %# dependent property, read-only
end
events (ListenAccess = public, NotifyAccess = private)
bPostSet %# custom event raised on b PostSet
end
methods
function this = BClass(aClass)
%# store AClass instance handle
this.aClassInst = aClass;
%# listen on PostSet event for property a of AClass instance
this.lh = addlistener(this.aClassInst, 'a', ...
'PostSet', @this.aPostSet_EventHandler);
end
function val = get.b(this)
val = this.aClassInst.a;
end
end
methods (Access = private)
function aPostSet_EventHandler(this, src, evt)
%# raise bPostSet event, notifying all registered listeners
notify(this, 'bPostSet')
end
end
end
基本上,我们将AClass的属性a
设置为可观察的。
接下来,在BClass的构造函数中,我们为传递来侦听属性a
更改的AClass实例注册一个侦听器。在回调中,我们通知该对象的侦听器b
也已更改
由于我们不能真正手动引发PostSet
,所以我创建了一个自定义事件bPostSet
,我们在上一个回调函数中引发了它。您始终可以自定义传递的事件数据,请参阅文档了解如何进行自定义。
下面是一个测试用例:
%# create the objects
a = AClass();
b = BClass(a);
%# change property a. We will not recieve any notification
disp('a.a = 1')
a.a = 1;
%# now lets listen for the 'bChanged' event on b
lh = addlistener(b, 'bPostSet',@(o,e) disp('-- changed'));
%# try to change the property a again. We shall see notification
disp('a.a = 2')
a.a = 2;
%# remove event handler
delete(lh)
%# no more notifications
disp('a.a = 3')
a.a = 3;
输出为:
a.a = 1
a.a = 2
-- changed
a.a = 3
请注意,当我们注册侦听器时,我们如何仅与BClass实例交互。当然,由于所有类都派生自handle
类,因此实例a
和私有属性aClassInst
都引用同一对象。因此,对a.a
的任何更改都会立即反映在b.aClassInst.a
上,这会导致内部aPostSet_EventHandler
执行,从而通知所有注册的侦听器我们的自定义事件。