How do I create a property in a class that is a direct handle to another class object
52 visualizaciones (últimos 30 días)
Mostrar comentarios más antiguos
Captain Karnage
el 16 de En. de 2023
Comentada: Kyle
el 20 de Oct. de 2023
Currently I have two classes, a class that contains a structured definition, and another class that is supposed to fit that definition. The myDefinition class loads the definition from a data file.
Here is the classdef and are some example properties from the definition class:
classdef myDefinition
properties(constant)
lengthMin = 8;
lenthMax = 64;
scaleDefault = [1 1];
end
properties( GetAccess = public, SetAccess = immutable )
type;
definitionFile;
map;
elements = {};
end
methods
function obj = myDefinition (thistype, filenameDefinition)
elementTable = readtable(filenameDefinition);
%Loops through elements here...
end
end
end
The function is really not important, but for an overview, the constructor for myDefinition reads the file in the path specified by the string or char array in filenameDefintion (which is a csv file), reads it in with readtable, and loops through the rows to create the elements.
I then have a class which has to follow one of the definitions in a myDefinition class.
classdef myDefinedObject
properties ( Access = private )
mindatabits = 0;
maxdatabits = 64;
end
properties ( SetAccess = immutable, GetAccess = public )
mydef;
type;
name;
a;
x;
end
methods
function obj = myDefinedObject( num, thisDefinition, data )
if isa(thisDefinition, 'myDefinition')
obj.mydef = thisDefinition;
else
obj.mydef = [];
warning('No valid definition file provided');
end
%Based on the number input, gets name and other properties from
%the definition, then parses the data based on the properties
%from the lookup in the definition
end
end
end
I have other methods in myDefinedObject other than the constructor, and the key point is that some of those methods need the definition that's stored in mydef. To determine what needs to be done. That is why I currently have mydef as an internal property of the myDefinedObject class.
In a typical run of my program, I have one or two objects of myDefinition (which can each be a very large object, memory wise) but could have dozens of objects of myDefinedObject. So, ideally, mydef would actually be a handle to a myDefinition object, rather than a copy of the object itself.
I know I can make a handle class
classdef myDefinitionHandle < handle
properties(setAccess = immutable)
def;
end
function obj = myDefinitionHandle(thisDefinition)
if isa(thisDefinition, 'myDefinition')
obj.def = thisDefinition;
else
obj.def = [];
warning('No valid definition file provided');
end
end
end
And then change myDefinedObject to check for a myDefinitionHandle and thus effectively have a handle to the definition.
However, the handle is now another layer away when trying to access it in my methods versus when I just stored the definition itself in every myDefinedObject object. For example, I have a method to check that a obj.a is correct based on the obj.type value and what is defined for that type in the definition:
function obj = comply(obj)
if strlength(obj.a) > obj.mydef.def.elements{obj.type}.max
obj.a = obj.a(1:obj.mydef.def.elements{obj.type}.max);
end
end
(Note: comply is a method that is called from set.a, and this is a very simplified example)
I would much prefer just to call obj.mydef.elements{obj.type}.max instead of obj.mydef.def.elements{obj.type}.max as the additional .def object layer in the class really serves no purpose.
Is there a better way I can structure these classes to make it so that mydef directly points to the thisDefinition object passed in the constructor?
4 comentarios
Chris
el 17 de En. de 2023
Editada: Chris
el 17 de En. de 2023
Does something like this work? I think you would have to set it like this in myDefinedObject, and the myDefinition object would have to exist (so, pass it to the constructor or setter). The handle is only set once--if something changes in the myDefinition object, the value obj.mydef.elements points to remains.
obj.mydef.elements = @() myDefinitionObj.elements
some background, in case you haven't seen it:
https://undocumentedmatlab.com/articles/handle-object-as-default-class-property-value
Kyle
el 20 de Oct. de 2023
Hi Captain Karnage,
I have another idea that might work for your use case which might not require the impressive subsref and subsasgn methods that you have written. To save on memory allocations and copying, you could have a single instance of all of your definitions stored in a separate singleton handle object. This singleton handle object would have a struct of all the definitions you want to keep track of. To add a new definition for it to keep track of, you could write a method.
To achieve your goal of calling obj.mydef.elements, you could make mydef a read-only dependent property of your myDefinedObject class. To keep track of the definition in all of your defined objects, you could pass in the DefinitionStore handle object and the name of the definition you care about to the myDefinedObject constructor. Storing them as private properties would give you the necessary information to implement the more convenient mydef get method.
This way, all of your defined objects have a reference to the one handle object that has copies of all of your definitions. Each one does not need to store its own definition, just the means of accessing it. With a dependent property, doing this access can be read-only and as a bonus, gets displayed for free as if it is a property.
Here is a mockup that I came up with in R2023b. It appears to work for all of the simple use cases I came up with.
classdef DefinitionStore < handle
properties (GetAccess=public, SetAccess=private)
Definitions (1,1) struct
end
methods
function obj = addDefinition(obj, definitionName, definition)
arguments
obj
definitionName string
definition myDefinition
end
% Can do other validation here
if obj.hasDefinition(definitionName)
warning("Definition name already exists");
return;
end
obj.Definitions.(definitionName) = definition;
end
function tf = hasDefinition(obj, definitionName)
tf = (isfield(obj.Definitions, definitionName));
end
end
end
classdef myDefinedObject
properties ( Access = private )
mindatabits = 0;
maxdatabits = 64;
DefinitionStore;
DefinitionName;
end
properties ( SetAccess = immutable, GetAccess = public )
type;
name;
a;
x;
end
properties (Dependent)
mydef;
end
methods
function obj = myDefinedObject( num, definitionStore, definitionName, data )
obj.DefinitionStore = definitionStore;
if definitionStore.hasDefinition(definitionName)
obj.DefinitionName = definitionName;
else
obj.DefinitionName = "";
warning('No valid definition provided');
end
%Based on the number input, gets name and other properties from
%the definition, then parses the data based on the properties
%from the lookup in the definition
end
function def = get.mydef(obj)
% Could add logic to
def = obj.DefinitionStore.Definitions.(obj.DefinitionName);
end
end
end
% Create a DefinitionStore with 2 definitions
store = DefinitionStore
store.addDefinition("DefinitionA", definitionA)
store.addDefinition("DefinitionB", definitionB)
% Add a few objects with access to their definitions
obj1 = myDefinedObject(1, store, "DefinitionA", "DataForObject1")
obj2 = myDefinedObject(2, store, "DefinitionA", "DataForObject2")
obj3 = myDefinedObject(3, store, "DefinitionB", "DataForObject3")
% Access elements of obj1's definition
theElements = obj1.mydef.elements;
% Display obj1's definition
obj1.mydef
All indexing you try to do to the definition will automatically be forwarded to the definition object. And you should also get tab completion for free on obj.mydef.*. The only downside that I can think of is that you don't get to show off the impressive RefObject class that you wrote!
Respuesta aceptada
Más respuestas (2)
Chris
el 18 de En. de 2023
Editada: Chris
el 18 de En. de 2023
@Captain Karnage like so?
mydef = myDefinition;
a = myDefinedObject([],mydef,[]);
whos mydef a
a.type
a.type()
Ver también
Categorías
Más información sobre Properties en Help Center y File Exchange.
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!