9

次のクラスがあるとします。

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

これは値をコピーするだけなので、機能しません。後でw.A変更しても変更されませんvalue

w.A別のハンドル クラスを作成せずに「ポインター」または「参照」を提供する方法はありますか? w.Aのようなものではなく、表記法を維持したいと思いw.A.valueます(その値を含むハンドルクラスを作成する必要があります)。

編集: 研究室で使用するコードをカプセル化するために、この機能を使用しています。私は、MATLAB と Arduino の間のインターフェースを設計して、空中および地上の乗り物を制御しています。vehicle.pwmMax" "、" " などにアクセスvehicle.flightCeilingして、基になるオブジェクト " vehicle.Globals.pwmMax.value" などをカプセル化することを望んでいました。

4

3 に答える 3

16

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 関数のオーバーロードを追加しました

于 2011-08-17T00:01:48.847 に答える
5

すべての制約を考えると、あなたが望むとおりに正確に機能するものはないと思います。

ただし、表記の問題についてはよくわかりません。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. もう1つの欠点は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

編集:これを実装する別の方法を考えました。これはより簡単です(つまり、元の投稿のクラスを保持するだけです)が、他の場所でコードを変更する必要があります。その背後にある基本的な考え方は最初のものと同じですが、オブジェクト自体にカプセル化することはありません (これにより、オブジェクトがよりクリーンになります)。

>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> w.A()
    15
>> value = @()(w.A);
>> value()
    15
>> w.A = 20;
>> value()
ans =
    20
于 2011-08-17T00:03:40.380 に答える
2

ハンドル クラスを使用しているため、例では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
于 2011-08-16T22:25:26.350 に答える