Overloading subsrefs for class and using it from another method in the same class

조회 수: 67 (최근 30일)
Cole
Cole 2024년 4월 1일 22:34
댓글: Matt J 2024년 4월 4일 1:11
Take the following example:
classdef testClass < handle
properties (Access = private)
number
end
methods
function obj = testClass(value)
obj.number = value;
end
function varargout = subsref(obj, S)
% Overload the "." operator for get access
switch S(1).type
case '.'
if strcmp(S(1).subs, 'number')
[varargout{1}] = obj.number+1; % We always add 1 to number
return;
elseif any(strcmp(S(1).subs, methods(obj)))
% If the field name is a method of the object,
% call the method using feval
if numel(S) > 1 && strcmp(S(2).type, '()')
% If there are arguments for the method, pass them
[varargout{1:nargout}] = feval(S(1).subs, obj, S(2).subs{:});
else
% Otherwise, just call the method
[varargout{1:nargout}] = feval(S(1).subs, obj);
end
return;
end
end
error('Not handled');
end
function print_number(obj)
num = obj.number; % Should call subsref?
fprintf('%g\n', num);
end
end
end
And I run this:
A = testClass(3);
Invalid expression. Check for missing multiplication operator, missing or unbalanced delimiters, or other syntax error. To construct matrices, use brackets instead of parentheses.
A.number; % This returns 4, as expected.
However:
A.print_number() % This prints "3".
How come A.print_number() does not print 4?
How do I code my subsref so that it can work properly when calling from a method of the same class?
I would like to be able to do A.number within testClass methods and have it call the overloaded subsref without having to define set.number or get.number methods. In my real use case, testClass would contain a containers.Map(). The subsref would over loaded so that testClass.key1 would return the value of the containers.Map() with a key of "key1".

채택된 답변

Matt J
Matt J 2024년 4월 2일 1:53
편집: Matt J 2024년 4월 2일 1:58
As an alternative to subsref or set/get, you could look at RedefinesDot, but I find that the newer, modular indexing can have surprising behavior, discussed extensively at,
  댓글 수: 4
Cole
Cole 2024년 4월 3일 19:58
Using RedefinesDot works for me. But your code only works R2022b or later. I tried in R2022a and it ran into a recursively loop but R2024a worked fine.
As a note to whoever is reading this:
dotReference/dotAssign is only called if you are accessing a variable that does not exist or is Access=private.
For example: if "test1" in the above example is a property of the class and it's not a private property, then A.test1 will not call dotReference, it will directly access "test1" property fro mthe class.
Other related questions on RedefinesDot:
Matt J
Matt J 2024년 4월 4일 1:11
Using RedefinesDot works for me. But your code only works R2022b or later.
That is strange. I implemented essentially the same thing in R2021b here with no difficulties:

댓글을 달려면 로그인하십시오.

추가 답변 (1개)

Matt J
Matt J 2024년 4월 2일 1:18
편집: Matt J 2024년 4월 2일 1:47
No, subsref is only called by indexing operations invoked outside the classdef. This design decision was to avoid triggering recursions when indexing operations are done inside the subsref method itself. For example, you would not want this instance of obj.number to call subsref,
[varargout{1}] = obj.number+1;
as that would cause an infinite recursion. I'm not sure why such recursions couldn't have been blocked the same way they are blocked for set/get methods, but that's just the way it's always been.
I would like to be able to do A.number within testClass methods and have it call the overloaded subsref without having to define set.number or get.number methods.
I don't know why you think avoiding set and get methods is beneficial. Probably for different reasons, though, I would also not recommend set/get methods for number. I would instead recommend introducing a Dependent property:
classdef testClass < handle
properties (Access = private)
number
end
properties (Dependent)
Number
end
methods
function obj = testClass(value)
obj.number = value;
end
function val=get.Number(obj)
val=obj.number+1;
end
function print_number(obj)
num = obj.Number; % Should call subsref?
fprintf('%g\n', num);
end
end
end
  댓글 수: 4
Cole
Cole 2024년 4월 2일 4:21
> Can you prove that calling the overloaded subs* methods won't ever introduce recursion in those other class methods? That there aren't any circumstances where the subs* methods would call back to the same methods that called them?
No, I can't prove this. I'm sure it can happen. I would argue that the recent mixin.indexing.Redefines causes just as much recursion if not handled properly, in the same way this will too.
Nevertheless, I see your point - but I think there should be flexibility in setting certain methods to allow for subsref/subsagn. Perhaps have it in the method (allowSubs) type of a block for an Abstract class.
Because what I'm getting stuck at is I'm trying to create an Abstract class that allows subclasses to overload a few specific methods. And in those methods, I can't see a case where there will be recursion would happen. Because the alternative is that I really can't define my Abstract class to be user-friendly, where subsref/subsagn would allow me to Abstract away a lot of the details of the implementation yet allowing the user to override certain methods. The best I can do right now is to have the class store function handles and ask the user to implement their function and set the function handles to their custom function. Which is not a great design scheme either...
Matt J
Matt J 2024년 4월 3일 10:40
편집: Matt J 2024년 4월 3일 18:02
Can you prove that calling the overloaded subs* methods won't ever introduce recursion in those other class methods? That there aren't any circumstances where the subs* methods would call back to the same methods that called them?
Even if there is no way to make subsref recursion-proof, the same danger already exists with property set/get methods, as in the example below. So, there is already precedent for making the scope of overloaded indexing method-specific. According to my conversations with @James Lebak, this nonuniformity in the scope of overloaded indexing is something MathWorks is trying to move away from. I'm not sure what the drawbacks of such nonuniformity are seen to be.
classdef myclass
properties
prop
end
methods
function val=get.prop(obj)
val=subget(obj);
end
function val=subget(obj)
val=obj.prop;
end
end
end

댓글을 달려면 로그인하십시오.

카테고리

Help CenterFile Exchange에서 Number Theory에 대해 자세히 알아보기

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by