在Matlab中,我可以定义一个类:
classdef klass < handle
properties(Dependent)
prop
end
end
即使没有为prop
定义一个getter,Matlab也非常乐意实例化这个类的对象。它只在我尝试访问它时失败(可以理解)。我想根据属性的名称动态设置GetMethod
。
不幸的是,即使属性是Dependent,meta.property
的{{1}}字段仍然是只读的。虽然继承自GetMethod
可以允许添加属性并以编程方式在每个实例中设置其GetMethod,但我认为它不能用于更改现有属性。我可能不得不走这条路,但由于dynamicprops
必须存在于每个对象,我更愿意在逐个类的基础上设置getter。这样的事情有可能吗?
另一种解决方案可能是通过某种全能方法。在其他语言中,这可以通过类似Ruby的method_missing
或类似PHP的__get()
来实现。但据我所知,Matlab中没有(记录或其他)模拟。
(我的用例:这个类由许多用户定义的子类继承,并且所有依赖属性都以类似的方式访问,只根据属性名称进行更改。而不是要求用户编写prop
包含对每个依赖属性的公共代码调用的方法,我想用包含必要元数据的匿名函数指针动态设置它们。
答案 0 :(得分:4)
以下是我的建议:在名为add_dyn_prop
的超类中创建一个方法。这个方法将在子类中调用,而不是以通常的方式创建dependent property。
这个想法是超类继承自dynamicprops
并使用addprop
到add一个新属性,并根据其名称手动设置其访问者方法。
classdef klass < dynamicprops
methods (Access = protected)
function add_dyn_prop(obj, prop, init_val, isReadOnly)
% input arguments
narginchk(2,4);
if nargin < 3, init_val = []; end
if nargin < 4, isReadOnly = true; end
% create dynamic property
p = addprop(obj, prop);
% set initial value if present
obj.(prop) = init_val;
% define property accessor methods
% NOTE: this has to be a simple function_handle (@fun), not
% an anonymous function (@()..) to avoid infinite recursion
p.GetMethod = @get_method;
p.SetMethod = @set_method;
% nested getter/setter functions with closure
function set_method(obj, val)
if isReadOnly
ME = MException('MATLAB:class:SetProhibited', sprintf(...
'You cannot set the read-only property ''%s'' of %s', ...
prop, class(obj)));
throwAsCaller(ME);
end
obj.(prop) = val;
end
function val = get_method(obj)
val = obj.(prop);
end
end
end
end
现在在子类中,我们不是通常的方式定义依赖属性,而是在构造函数中使用这个新的继承函数来定义动态属性:
classdef subklass < klass
%properties (Dependent, SetAccess = private)
% name
%end
%methods
% function val = get.name(obj)
% val = 'Amro';
% end
%end
methods
function obj = subklass()
% call superclass constructor
obj = obj@klass();
% define new properties
add_dyn_prop(obj, 'name', 'Amro');
add_dyn_prop(obj, 'age', [], false)
end
end
end
输出:
>> o = subklass
o =
subklass with properties:
age: []
name: 'Amro'
>> o.age = 10
o =
subklass with properties:
age: 10
name: 'Amro'
>> o.name = 'xxx'
You cannot set the read-only property 'name' of subklass.
当然,您现在可以根据最初预期的属性名称自定义getter方法。
根据评论,请在下面找到上述相同技术的细微变化。
这个想法是要求子类创建一个属性(在超类中定义为abstract),其中包含要创建的所需动态属性的名称。然后,超类的构造函数将创建指定的动态属性,将其访问器方法设置为泛型函数(可以根据您请求的属性名称自定义其行为)。我正在重用我之前提到的add_dyn_prop
函数。
在子类中,我们只需要实现继承的抽象dynamic_props
属性,使用名称列表初始化(如果您不想创建任何动态属性,则使用{}
)。例如,我们写道:
classdef subklass < klass
properties (Access = protected)
dynamic_props = {'name', 'age'}
end
methods
function obj = subklass()
obj = obj@klass();
end
end
end
超类与之前的类似,只是现在它有责任在每个属性名称的构造函数中调用add_dyn_prop
:
classdef klass < dynamicprops % ConstructOnLoad
properties (Abstract, Access = protected)
dynamic_props
end
methods
function obj = klass()
assert(iscellstr(obj.dynamic_props), ...
'"dynamic_props" must be a cell array of strings.');
for i=1:numel(obj.dynamic_props)
obj.add_dyn_prop(obj.dynamic_props{i}, [], false);
end
end
end
methods (Access = private)
function add_dyn_prop(obj, prop, init_val, isReadOnly)
% input arguments
narginchk(2,4);
if nargin < 3, init_val = []; end
if nargin < 4, isReadOnly = true; end
% create dynamic property
p = addprop(obj, prop);
%p.Transient = true;
% set initial value if present
obj.(prop) = init_val;
% define property accessor methods
p.GetMethod = @get_method;
p.SetMethod = @set_method;
% nested getter/setter functions with closure
function set_method(obj,val)
if isReadOnly
ME = MException('MATLAB:class:SetProhibited', sprintf(...
'You cannot set the read-only property ''%s'' of %s', ...
prop, class(obj)));
throwAsCaller(ME);
end
obj.(prop) = val;
end
function val = get_method(obj)
val = obj.(prop);
end
end
end
end
注意:我没有使用ConstructOnLoad
类属性或Transient
属性属性,因为我仍然不确定它们如何影响从动态属性的已保存MAT文件中加载对象。
>> o = subklass
o =
subklass with properties:
age: []
name: []
>> o.name = 'Amro'; o.age = 99
o =
subklass with properties:
age: 99
name: 'Amro'
答案 1 :(得分:2)
检查这是否是您想要的。问题是用户需要使用()获取属性,这可能很无聊,但无论如何,我认为这样你可以改变变量。您不能直接在类上更改它们,但可以根据需要更改对象属性值。它不需要更改构造函数的值,您可以使用将由类继承的另一个函数来执行此操作。
klass1.m
classdef(InferiorClasses = {?klass2}) klass < handle
methods
function self = klass
selfMeta = metaclass(self);
names = {selfMeta.PropertyList.Name};
for name = names
switch name{1}
case 'prop_child_1'
self.(name{1}) = @newGetChild1PropFcn;
case 'prop_child_2'
self.(name{1}) = @newGetChild2PropFcn;
end
end
end
end
methods(Static)
function out = prop
out = @defaultGetPropFcn;
end
end
end
function out = defaultGetPropFcn
out = 'defaultGetPropFcn';
end
function out = newGetChild1PropFcn
out = 'newGetChild1PropFcn';
end
function out = newGetChild2PropFcn
out = 'newGetChild2PropFcn';
end
klass2.m
classdef klass2 < klass
properties
prop_child_1 = @defaultGetChildPropFcn1
prop_child_2 = @defaultGetChildPropFcn2
end
methods
function self = klass2
self = self@klass;
end
end
end
function out = defaultGetChildPropFcn1
out = 'defaultGetChildPropFcn1';
end
function out = defaultGetChildPropFcn2
out = 'defaultGetChildPropFcn2';
end
输出:
a = klass2
b=a.prop_child_1()
b =
newGetChild1PropFcn