假设我有以下课程:
classdef myClass < handle
properties
A = 1
end
methods
function obj = myClass(val)
obj.A = val;
end
end
end
假设我实例化了这个类的一个实例,然后稍微操作它然后复制它。由于它是一个句柄类,“副本”实际上只是相同对象的另一个实例:
>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> disp(w.A)
15
但是我想看A
而不需要实例化myClass。天真地做着
>> value = w.A
不起作用,因为这只是复制值;稍后改变w.A
不会改变value
。
有没有办法向w.A
提供“指针”或“引用”而无需创建单独的句柄类?我宁愿保留符号w.A
而不是w.A.value
(我必须创建句柄类以包含该值)。
编辑:我正在使用此功能以帮助封装我的代码以供我的研究实验室使用。我正在设计MATLAB和Arduino之间的接口来控制空中和地面车辆;我希望能够访问“vehicle.pwmMax
”,“vehicle.flightCeiling
”等内容来封装基础对象:“vehicle.Globals.pwmMax.value
”等。
答案 0 :(得分:16)
您可以使用PropertyReference类
来完成此操作classdef PropertyReference < handle
%PropertyReference Reference to a property in another object
properties
sourceHandle
sourceFieldName
end
properties (Dependent = true)
Value
end
methods
function obj = PropertyReference (source, fieldName)
obj.sourceHandle = source;
obj.sourceFieldName = fieldName
end
function value = get.Value( obj )
value = obj.sourceHandle.(obj.sourceFieldName);
end
function set.Value( obj, value )
obj.sourceHandle.(obj.sourceFieldName) = value;
end
function disp( obj )
disp(obj.Value);
end
end
end
继续您的示例,您可以按如下方式使用PropertyReference:
q = myClass(10);
>> q.A = 15;
>> ref = PropertyReference(q,'A');
>> disp(ref)
15
>> q.A = 42;
>> disp(ref)
42
使用PropertyReference类有点尴尬,但原始类保持不变。
编辑 - 根据strictrude27评论添加了disp函数重载
答案 1 :(得分:5)
考虑到你所有的限制条件,我认为没有任何东西能完全符合你的要求。
但是,我对你的符号问题并不十分清楚。当您考虑w.A
没有改变时,为什么要保留符号value
?保持符号w.A
相似并不是一个真正的问题。
使用一些修改过的代码,我可以产生以下执行:
>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> w.A
15
>> value = w.Aref;
>> value()
15
>> w.A = 20;
>> value()
ans =
20
但是没有办法围绕符号value()
,因为这是实施的转折点;我认为是最接近你想要的东西。当您使用以下代码实现myClass
时,您将获得上述行为:
classdef myClass < handle
properties
A = 1;
end
methods
function obj = myClass(val)
obj.A = val;
end
function a = Aref(obj)
a = @()(obj.A);
end
end
end
所以你看到Aref
方法实际上返回一个从对象中获取值的函数句柄。这也意味着此引用是只读的!
另请注意,在能够获得myClass
的值之前,您必须实例化A
实例(否则,从哪里获得A
的值?)。此实例不必在当前工作空间(例如另一个函数范围)内可见,因为myClass实例存储在函数句柄value
中。
这个方法的缺点是你只得到一个只读引用,你将不得不使用调用value()
来获取实际值而不是函数句柄(这样就改变了符号,但不是你希望保留一个(或者至少可以通过A
替换我的代码中的Aval
并将Aref
重命名为A
来实现。另一个缺点是解决问题value
可能比简单地解析一个变量慢一点(这是否会影响你value()
的用法)。
如果您想要更改某些符号,可以使用dependent属性来完成:
classdef myClass < handle
properties (Access=private)
Aval = 1;
end
properties (Dependent)
A;
end
methods
function obj = myClass(val)
obj.A = val;
end
function a = get.A(obj)
a = @()(obj.Aval);
end
function set.A(obj,value)
obj.Aval = value;
end
end
end
上述等效执行由下式给出:
>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> w.A()
15
>> value = w.A;
>> value()
15
>> w.A = 20;
>> value()
ans =
20
编辑我想到了另一种方法来实现这一点,这更简单(即只保留原始帖子的类),但它要求您更改其他地方的代码。它背后的基本思想与第一个相同,但没有将它封装在对象本身(这使得对象更清晰,恕我直言)。
>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> w.A()
15
>> value = @()(w.A);
>> value()
15
>> w.A = 20;
>> value()
ans =
20
答案 2 :(得分:2)
由于您使用的是handle class,因此示例中的q
和w
都会引用内存中的相同对象;它们本身就是它们所代表的对象的“指针”/“参考”。
继续你的例子,如果你改变一个,它将反映在另一个。
>> q = myClass(10);
>> w = q;
>> q.A = 99;
>> disp(w.A)
99
另请注意,在致电w = q;
时,您不会创建该课程的其他实例。在内存空间方面比较以下示例:
>> q = myClass(rand(7000));
>> m = memory; disp(m.MemUsedMATLAB)
792870912
>> w = q;
>> m = memory; disp(m.MemUsedMATLAB)
792834048
反对:
>> q = myClass(rand(7000));
>> w = myClass(rand(7000));
??? Error using ==> rand
Out of memory. Type HELP MEMORY for your options.
玩弄这个,我提出了以下的hackish解决方案。
首先,我们围绕类构造函数创建一个包装器函数。它像往常一样创建一个对象,并且它返回一个函数句柄,该函数句柄充当与使用“PostSet”事件监听器与原始对象属性同步的闭包变量的只读访问器。
对原始类的唯一更改是添加SetObservable
属性属性:
classdef myClass < handle
properties (SetObservable)
A
end
methods
function obj = myClass(val)
obj.A = val;
end
end
end
function [w A] = myClassWrapper(varargin)
w = myClass(varargin{:});
A = @getWA;
%# closure variable
a = w.A;
%# add listener to when w.A changes
addlistener(w, 'A', 'PostSet',@changeCallback);
function val = getWA()
%# return the value of the closure variable
val = a;
end
function changeCallback(obj,ev)
%# update the closure variable
a = ev.AffectedObject.A;
%#fprintf('Value Changed to %g\n',a)
end
end
现在我们可以将包装器用作:
>> [w a] = myClassWrapper(10);
>> a()
ans =
10
>> w.A = 99;
>> a()
ans =
99