5

在 Matlab 中,我可以这样定义一个类:

classdef klass < handle
    properties(Dependent)
        prop
    end
end

Matlab 非常乐意实例化此类的对象,即使没有为prop. 只有当我尝试访问它时它才会失败(可以理解)。我想GetMethod根据属性的名称动态设置。

不幸的是,即使属性是 Dependent,meta.property字段 forGetMethod仍然是只读的。虽然继承自dynamicprops可以允许在每个实例中添加属性并以编程方式设置其 GetMethod,但我不相信它可以用于更改现有属性。我可能不得不走这条路,但是对于每个对象都prop 必须存在,我更愿意简单地逐个类地设置getter。这样的事情可能吗?

另一种解决方案可能是通过某种包罗万象的方法。在其他语言中,这可以通过类似 Rubymethod_missing或类似 PHP 的__get(). 但据我所知,Matlab 中没有(记录或其他)模拟。


(我的用例:这个类被许多用户定义的子类继承,并且它们的所有依赖属性都以类似的方式访问,仅根据属性名称进行更改。而不是要求用户编写get.*包装对公共代码的调用的方法对于它们的每一个依赖属性,我想使用包含必要元数据的匿名函数指针来动态设置它们)。

4

2 回答 2

5

这是我的建议:在超类中创建一个名为add_dyn_prop. 该方法将在子类中调用,而不是像通常那样创建依赖属性。

这个想法是超类继承自dynamicprops并用于addprop添加属性,并根据其名称手动设置其访问器方法。

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 方法。


编辑:

根据评论,请在下面找到上面讨论的相同技术的轻微变化。

这个想法是要求子类创建一个属性(在超类中定义为抽象),其中包含要创建的所需动态属性的名称。然后,超类的构造函数将创建指定的动态属性,将它们的访问器方法设置为通用函数(可以根据您的要求根据属性名称自定义它们的行为)。我正在重用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'
于 2013-08-17T18:55:24.673 回答
2

检查这是否是您想要的。问题是用户需要使用 () 来获取属性,这可能很无聊,但无论如何,我认为这样你可以更改变量。您不能直接在类上更改它们,但您可以根据需要更改对象的属性值。它不需要更改构造函数上的值,您可以使用将由类继承的另一个函数来做到这一点。

类1.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

类2.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
于 2013-08-15T19:01:16.367 回答