【问题标题】:MATLAB - create reference (handle?) to variableMATLAB - 创建对变量的引用(句柄?)
【发布时间】:2011-10-28 11:21:47
【问题描述】:

假设我有以下课程:

classdef myClass < handle
    properties
        A = 1
    end
    methods
        function obj = myClass(val)
            obj.A = val;
        end
    end
end

假设我实例化了这个类的一个实例,然后稍微操作它,然后复制它。因为它是一个句柄类,所以“副本”实际上只是 same 对象的另一个实例:

>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> disp(w.A)
   15

但我想观看 A 而无需实例化 myClass。天真地做着

>> value = w.A

不起作用,因为这只是复制值; changningw.A以后不会改value

有没有办法为w.A 提供“指针”或“引用”而无需创建单独的句柄类?我宁愿保留符号 w.A 而不是 w.A.value 之类的符号(我必须创建句柄类来包含该值)。

编辑:我使用此功能是为了帮助封装我的代码以供我的研究实验室使用。我正在设计 MATLAB 和 Arduino 之间的接口来控制空中和地面车辆;我希望访问“vehicle.pwmMax”、“vehicle.flightCeiling”等内容来封装底层对象:“vehicle.Globals.pwmMax.value”等。

【问题讨论】:

  • 我认为答案是否定的;我相信您需要保留对类实例的引用,并在每次要引用实例变量 A 时执行 foo.A
  • @strictlyrude27:如果你能解释一下你打算如何使用它,可能有更好的方法来做到这一点......
  • 更新了原帖来解释我的意图

标签: matlab oop matlab-class


【解决方案1】:

你可以通过 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 函数重载

【讨论】:

  • 看起来是一个非常好的实现。我(个人)唯一不太喜欢它的是字符串参数'A',但我想这是一个偏好问题(无论如何,MATLAB 对自动完成字段的支持有限,没有办法解决这个问题使用这种实现的字符串参数)。
  • 我喜欢这个,这可以做得很好。此外,我可以重载disp() 以立即显示ref.value
  • @RAAC - 对从句柄派生的对象的引用吗? 2015a 没试过,但在 2014a 仍然有效。
【解决方案2】:

考虑到您的所有限制,我认为没有什么可以完全按照您的意愿行事。

但是,我对您的符号问题并不是很清楚。为什么要保留符号 w.A 而您认为 value 不会改变?保持符号 w.Asimilar 不是真正的问题。

使用一些修改后的代码,我可以产生以下执行:

>> 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

【讨论】:

    【解决方案3】:

    由于您使用的是handle class,因此您的示例中的qw 都指代内存中的同一个对象;它们本身就是它们所代表的对象的“指针”/“引用”。

    所以继续您的示例,如果您对其中一个进行更改,它将反映在另一个中。

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

    编辑

    玩弄这个,我想出了以下骇人听闻的解决方案。

    首先,我们围绕类构造函数创建一个包装函数。它像往常一样创建一个对象,并返回一个函数句柄,该函数句柄充当对使用“PostSet”事件侦听器与原始对象属性同步的闭包变量的只读访问器。

    对原始类的唯一更改是添加SetObservable 属性属性:

    myClass.m

    classdef myClass < handle
        properties (SetObservable)
            A
        end
        methods
            function obj = myClass(val)
                obj.A = val;
            end
        end
    end
    

    myClassWrapper.m

    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
    

    【讨论】:

    • 对,这就是我的意思 - 我想引用一个特定属性 q.A,而不必将其他内容保留在q 中。所以像a = q.A 这样的赋值,但是如果q.A 改变,a 也会改变。由于q.A 是一个值而不是句柄,所以a 不会在q.A 发生变化时发生变化。
    • @strictlyrude27: AFAIK 你不能有一个简单的(双)变量来跟踪对象属性的值,但我可能错了..
    猜你喜欢
    • 2012-05-05
    • 1970-01-01
    • 1970-01-01
    • 2014-10-27
    • 2012-09-26
    • 2017-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多