MATLAB - создать ссылку (дескриптор?) На переменную - PullRequest
9 голосов
/ 17 августа 2011

Предположим, у меня есть следующий класс:

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

Скажем, я создаю экземпляр этого класса, затем немного манипулирую им, а затем копирую его.Поскольку это класс дескриптора, «copy» - это просто еще один экземпляр того же объекта:

>> 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" и т. Д.

Ответы [ 3 ]

16 голосов
/ 17 августа 2011

Вы можете сделать это с помощью класса 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 немногонеудобно, но оригинальный класс остается неизменным.

РЕДАКТИРОВАТЬ - Добавлена ​​перегрузка функции disp в соответствии со строгим комментарием 27

5 голосов
/ 17 августа 2011

Я не думаю, что есть что-то, что будет делать именно так, как вы хотите, учитывая все ваши ограничения.

Однако я не совсем понимаю ваши нотационные проблемы. Почему вы хотите сохранить нотацию 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()).

Если вы хотите изменить некоторые нотации, это можно сделать с помощью зависимых свойств:

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
2 голосов
/ 17 августа 2011

Поскольку вы работаете с классом дескриптора , и 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.

РЕДАКТИРОВАТЬ

Играя с этим, я придумалследующее хакерское решение.

Сначала мы создаем функцию-оболочку вокруг конструктора класса.Он создает объект, как обычно, плюс возвращает дескриптор функции, который действует как средство доступа только для чтения к переменной закрытия, синхронизированной с исходным свойством объекта, с помощью прослушивателя событий 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
...