1

I want to load a property on demand but i cant get it working. I have a class with a transient property foo. So the property is not stored when the object is saved. When I use a method that is calling the property 'foo', I want that the value of foo is loaded from a separate mat file and stored into the object as long as it is in workspace.

I tried something with get and set methods but cant get it working. Is this possible? Or do I always add a line of code that is loading the property? The following code does not do what I want but it gives an indication what i tried.

In addition, the code below keeps loading the foo.mat file when the property is used. I want to load foo.mat only one time and store it as a property and retrieve the data from there instead of loading. The reason for my question is that the foo property is rather large i.e. a class with many properties in itself. I only want to load it when it is needed and dont want to store it in foobar class itself.

classdef foobar
    properties(Transient = true)
        foo
    end
    methods
        function value = get.foo(obj)
            if isempty(obj.foo)
                value = load('foo.mat');
                disp('load foo.mat');
            end
        end
        function obj = set.foo(obj,value)
            obj.foo = value;
        end
    end
end
S Geurts
  • 118
  • 7
  • 2
    "Can't get it working" means...what exactly? Your example works fine. – sco1 Jul 16 '18 at 17:44
  • Good point to explain it better. Using the script I posted, every time i execute for example, object.foo it loads the foo.mat file instead of only once. – S Geurts Jul 16 '18 at 19:24
  • A [Default Property](https://www.mathworks.com/help/matlab/matlab_oop/specifying-properties.html#brqy3km-10) might be a better option. – sco1 Jul 16 '18 at 19:26
  • I dont want a default property as it is pretty large. I only want to load it when it is called, and load it once. – S Geurts Jul 16 '18 at 19:38

1 Answers1

1

You have two major problems here:

  1. In your get.foo method, once you load the value, you never update the value of foo in the object, so it remains empty.

  2. Even if you tried to update foo in your get.foo method, it would still be empty in the original object because your foobar class is a value class. Methods that modify a value class object have to return the modified object as an output, because they are essentially modifying a copy of the object. A set method of a value class returns a modified object that is used to overwrite the original object, but get methods don't return modified objects (since they aren't generally expected to modify them). To get around that limitation, you'll need the reference-like behavior of a handle class (here's a related question you may want to take a look at for more background).

So, in order to get the behavior you want you'd have to implement foobar as a subclass of the handle class and update the foo field when you first load it:

classdef foobar < handle    % Inherit from handle class

  properties(Transient = true)
    foo
  end

  methods

    function value = get.foo(obj)
      if isempty(obj.foo)
        value = load('foo.mat');
        disp('load foo.mat');
        obj.foo = value;       % Update foo
      end
      value = obj.foo;         % Return current foo value
    end

    function set.foo(obj, value)  % Return value is unnecessary for handle class
      obj.foo = value;
    end

  end

end

This should now give you the behavior you want (i.e. foo is only loaded when it is first accessed).

Note: Any method that invokes get.foo will initialize foo. One method you may overlook, because it's created by default for a class, is the disp method. The default display for a class object will show the class name followed by a list of non-hidden public properties and their values. Note what happens when I create an object of class foobar from above with and without a semicolon:

>> f = foobar;  % Display is suppressed
>> f = foobar

f = 

load foo.mat               % foo gets initialized...
  foobar with properties:

    foo: [1×1 struct]      % ...because its value is displayed here

If you want to avoid this, you can overload the disp function for your foobar object so that displaying the object doesn't access (and thus initialize) foo. For example, you can add this method to the above foobar class:

    function disp(obj)
      disp('     foobar object');
    end

Now you won't initialize foo when displaying the object:

>> f = foobar

f = 

     foobar object  % foo not yet initialized
>> a = f.foo;
load foo.mat        % foo initialized because we accessed it
gnovice
  • 125,304
  • 15
  • 256
  • 359
  • Your answer is almost what I want but the last note is an issue for me. Most of my methods, which call the property `foo`, live insite the object. I only want to load `foo` if a method is calling for it, so that the property `foo` is not loaded while an instance of `foobar` already exists. – S Geurts Jul 18 '18 at 15:45
  • 1
    @SGeurts: I added more detail to my note. All you should have to do is overload the `disp` method so that it doesn't access `foo`. – gnovice Jul 18 '18 at 16:22
  • Great! The set method isnt even necessary for my purpose. I also discovered that showing the object in the Variables menu initiate loading `foo`. What I still dont understand is why a Handle class is needed instead of a Value class. – S Geurts Jul 18 '18 at 16:55
  • 1
    @SGeurts: I added some more detail about value and handle classes. Hope that makes it clearer. – gnovice Jul 18 '18 at 17:26
  • Is there also a way to prevent loading the `foo` data while the Variables display is showing the `foobar` object? – S Geurts Jul 19 '18 at 12:52
  • 1
    I can set the property attribute Hidden to 'true', so it is solved. – S Geurts Jul 19 '18 at 13:50