MATLAB - 创建变量的引用(句柄?)

发布于 2024-11-30 10:39:17 字数 905 浏览 1 评论 0原文

假设我有以下类:

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

是行不通的,因为这只是复制了值; changning wA 以后不会改变value

有没有一种方法可以为 wA 提供“指针”或“引用”,而无需创建单独的句柄类?我宁愿保留符号 wA 而不是类似 wAvalue 的符号(我必须创建句柄类来包含该值)。

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

Suppose I have the following class:

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

Say I instantiate an instance of this class, then manipulate it slightly and then copy it. As it's a handle class, the "copy" is really just another instance of the same object:

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

But I would like to watch A without needing to instantiate myClass. Naively doing

>> value = w.A

doesn't work, since this just copies the value; changning w.A later on will not change value.

Is there a way to provide a "pointer" or "reference" to w.A without having to create a separate handle class? I'd rather keep the notation w.A rather than something like w.A.value (with me having to create the handle class to contain that value).

EDIT: I am using this functionality in order to help encapsulate my code for use with my research lab. I am designing an interface between MATLAB and Arduino to control air and ground vehicles; I was hoping to access stuff like "vehicle.pwmMax", "vehicle.flightCeiling", etc, to encapsulate the underlying object: "vehicle.Globals.pwmMax.value", etc.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

酒几许 2024-12-07 10:39:17

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

You could do this by with a PropertyReference class

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

Continuing your example, you could then use PropertyReference as follows:

q = myClass(10);
>> q.A = 15;
>> ref = PropertyReference(q,'A');
>> disp(ref)
   15
>> q.A = 42;
>> disp(ref)
   42

Usage of the PropertyReference class is a bit awkward but the original class remains unchanged.

EDIT - Added disp function overload as per strictlyrude27 comment

东风软 2024-12-07 10:39:17

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

但是,我不太清楚您的符号问题。当您考虑 value 不变时,为什么要保留符号 wA ?保持符号 wA 相似并不是一个真正的问题。

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

>> 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() 来获取实际值而不是函数句柄(这样就改变了表示法,但是不是您想要保留的那个(或者至少可以通过用 Aval 替换我的代码中的 A 并将 Aref 重命名为 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

edit: 我想到了另一种方法来实现这个,它更简单(即只保留类你原来的帖子),但它要求你更改其他地方的代码,其背后的基本思想与第一个相同,但不将其封装在对象本身中(这使得对象更干净,恕我直言)。

>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> w.A()
    15
>> value = @()(w.A);
>> value()
    15
>> w.A = 20;
>> value()
ans =
    20

I don't think there is anything that will do exactly as you want, given all your constraints.

However, I'm not really clear on your notational issues. Why do you want to retain the notation w.A while you are considered about value not changing? Keeping the notation w.A similar is not a real issue.

Using some modified code, I can produce following execution:

>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> w.A
    15
>> value = w.Aref;
>> value()
    15
>> w.A = 20;
>> value()
ans =
    20

But there is no way around the notation value() as that is the turning point of the implementation; which I think is the closest you can get to what you want. You get the behavior above when you use the following code to implement 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

So you see that the Aref method actually returns a function handle which fetches the value from the object. This also means that this reference is read-only!

Also note that you will have to instantiate a myClass instance before you are able to get the value of A (where would you get the value of A from otherwise?). This instance does not have to be visible inside your current workspace (e.g. another function scope), since the myClass instance is stored within the function handle value.

Drawback of this method is that you only get a read-only reference, you will have to use the call value() to get the actual value instead of the function handle (so that changes the notation, but not the one you wanted to keep (or at least it can be made so by substituting A in my code by Aval and renaming Aref to A). Another drawback is that resolving value might be a bit slower than simply resolving a variable (whether that's a problem will depend on your usage of value()).

If you want some of the notations changed, this can be done by using dependent properties:

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

The equivalent execution of above is given by:

>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> w.A()
    15
>> value = w.A;
>> value()
    15
>> w.A = 20;
>> value()
ans =
    20

edit: I thought of another way to implement this, which is simpler (i.e. just keep the class of your original post) but it requires you to change the code in other places. The basic idea behind it is the same as the first ones, but without encapsulating it in the object itself (which makes the object cleaner, IMHO).

>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> w.A()
    15
>> value = @()(w.A);
>> value()
    15
>> w.A = 20;
>> value()
ans =
    20
开始看清了 2024-12-07 10:39:17

由于您正在使用 句柄类,因此 q<示例中的 /code> 和 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.

编辑

对此,我想出了以下黑客解决方案。

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

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

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

Since you are working with a handle class, both q and w in your example refer to the same object in memory; they are themselves a "pointer"/"reference" to the object they represent.

So continuing your example, if you make changes to one, it will be reflected in the other.

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

Also note that you are not creating another instance of the class when you call w = q;. Compare the following examples in terms of memory space:

>> q = myClass(rand(7000));
>> m = memory; disp(m.MemUsedMATLAB)
   792870912
>> w = q;
>> m = memory; disp(m.MemUsedMATLAB)
   792834048

Against:

>> q = myClass(rand(7000));
>> w = myClass(rand(7000));
??? Error using ==> rand
Out of memory. Type HELP MEMORY for your options.

EDIT

Playing around with this, I came up with the following hackish solution.

First we create a wrapper function around the class constructor. It creates an object as usual, plus it returns a function handle that acts as a read-only accessor to a closure variable synced with the original object property using a "PostSet" events listener.

The only change to the original class is to add the SetObservable property attribute:

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

Now we can use the wrapper as:

>> [w a] = myClassWrapper(10);
>> a()
ans =
    10
>> w.A = 99;
>> a()
ans =
    99
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文