So I have Class
class MyClass
....do things...
and I add objects to it with
ObjName = MyClass(things)
and my problem is that when I add ObjName to MyClass in a loop, I can't figure out a way to create a new object name each loop so it keeps overwriting the only Obj this ends up creating. I tried adding a list as in
ObjName[i] = MyClass(things)
but it wouldn't work.
Here is what I'm trying to do specifically (edited for clarity):
So when I add objects to MyClass, the name of the object added should be callable with input, like so:
somename = input("objname: ") # User input decides how the object values can be called
TempObjName = MyClass(things) # Values of the specific object, will contain more than one unique object
*...*
somename.someattribute() ## 2 different
somename2.someattribute() ## values, sets or etc
Try this:
ObjList = []
for i in whatever:
temp = MyClass(things)
ObjList.extend(temp)
I wasn't able to create the function I wanted per-say, but because my code had a dictionary that saved the obj name and obj value in it, I was able to do what I initially wanted to do by rerunning the requested name from the dictionary in the Class.
Related
In this article (https://blogs.mathworks.com/loren/2012/07/16/who-what-why-but-not-this/) near the bottom Loren says that class properties can be the same as keywords. However, how is this possible? If you write a classdef script any attempt to use a keyword (including class keywords like "events") in the properties block gets a red syntax error. Was she mistaken? I'm asking because I really want a property name to be a keyword for a particular application.
Its possible by the use of dynamic properties, for example:
classdef test < dynamicprops
methods
function obj = test()
end
end
end
var = test();
var.addprop ( 'events' );
var.events = 123;
It can make code harder to maintain and its a bit overkill if you only want to name a single property the same as a keyword, in that instance why dont you do something like capitilizing the var name, or prepending it with something - so it still reads like what you want but it doesn't cause the name clash:
classdef test
properties
Events
myIf
% etc...
end
methods
function obj = test()
end
end
end
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
You have two major problems here:
In your get.foo method, once you load the value, you never update the value of foo in the object, so it remains empty.
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
My Objective is:
Using MATLAB, set the property value within one class method, and the property values are different between instances.
My Problem is:
When using SET in the class method, I will change the property value of all instances of this class, which is not what I want. I only want to change the property value of this instance.
About the dynamic property: I think it's used to create a unique property of the instance instead of setting the unique value of a general class property, is that right?
Code example:
classdef Storage
properties
tree = containers.Map('KeyType','int32', 'ValueType','any')
end
methods
function obj = set_tree(obj,period, value)
obj.tree(period) = value;
end
end
end
When setting the value using this method:
st1 = Storage();
st2 = Storage();
st1 = st1.set_tree(10,1);
st2 = st2.set_tree(10,2);
Right now, the value set to st2.tree(10) will override the value set to st1.tree(10), which I am trying to avoid.
The problem you're having is caused by setting a handle class object as a default value for a class property. The relevant documentation says this:
MATLAB® evaluates property default values only once when loading the class. MATLAB does not reevaluate the assignment each time you create an object of that class. If you assign an object as a default property value in the class definition, MATLAB calls the constructor for that object only once when loading the class.
So, for your Storage class above, all of your instances will be using the same default containers.Map object stored in the tree property. And since the containers.Map class is a subclass of the handle class it has reference behavior, which means the copies of the object will all point to the same underlying key/value map. If you want independent objects for each instance, you can initialize the tree property in the constructor:
classdef Storage
properties
tree
end
methods
function obj = Storage()
obj.tree = containers.Map('KeyType','int32', 'ValueType','any');
end
function obj = set_tree(obj, value, period)
obj.tree(period) = value;
end
end
end
I'm working with treeviews in Powershell - I have a different node for each of our buildings. In my code I'm grabbing variables, Joining them together, and using that as the variable name - but my code is seeing the variable as a string instead of the name of a node that already exists... so I'm getting
You cannot call a method on a null-valued expression.
How can I do this? It would save me from hard-coding in every floor in every building. Here's what my code looks like:
$bldg = "SG1-1" //for example
function refreshPrinterList ( $bldg )
{
$bldg1 = $bldg.substring(0,3)
$floor = $bldg.substring(4,1)
$refreshNode = -join('$TreeNode_',$bldg1,'_',$floor)
$refreshNode.gettype() //Returns System.String`
if($bldg1 -eq "SG1") {
if($floor -eq "1") {
$count = $refreshNode.Nodes.Count
while($refreshNode.Nodes.Count -gt 0)
{
$refreshNode.Nodes.RemoveAt($count)
$count--
}
The -join operator is for strings, and dutifully gives you one back instead of a TreeNode that you want. If you are passing in a string ($bldg looks like a string from your example), then you can do all the string manipulation you want, but there is no TreeNode object in that function to assign a name to. So, we need to make a TreeNode that your function could use. What about something like this?
$newNodeName = -join('$TreeNode_',$bldg1,'_',$floor)
$refreshNode = New-Object System.Windows.Forms.TreeNode($newNodeName )
// do stuff with $refreshNode as it is a TreeNode object like you expect
This $refreshNode will have no Nodes inside of it since we just fabbed it up. Since it looks like you want to modify an existing TreeNode object, pass in the $refreshNode as an argument then modify its friendly description with the $newNodeName.
I was pointed in the right direction over on the Technet Social forum
My question on Technet
The answer was using 'Get-Variable'
I had the two variables $bldg1 and $floor which I joined into a string:
$newNodeName = -join('TreeNode_',$bldg1,'_',$floor)
and then I passed that using 'Get-Variable' - but I had to put the variable name within parantheses, like so:
$refreshNode = (Get-Variable ($newNodeName)).Value
Now, instead of returning a string type it returns my existing string!
I have a custom class module in VBA (Access) that is supposed to handle a large amount of external data. Currently I have two functions Read(name) and Write(name, value) that allows to read and set dynamic properties.
Is there a way to define a more syntactic way to read and write those data? I know that some objects in VBA have a special way of accessing data, for example the RecordSet, which allows to read and set data using myRS!property_name. Is there a way to do exactly the same for custom class modules?
The exclamation mark syntax is used to access members of a Scripting.Dictionary instance(you'll need to add a reference to Microsoft Scripting Runtime through Tools > References first). To use this syntaxyou'll need to be storing the information internally in a dictionary.
The quickest way to use it in a class is to give your class an object variable of type Scripting.Dictionary and set it up as follows:
Option Explicit
Dim d As Scripting.Dictionary
Private Sub Class_Initialize()
Set d = New Scripting.Dictionary
End Sub
Private Sub Class_Terminate()
Set d = Nothing
End Sub
Public Property Get IntData() As Scripting.Dictionary
Set IntData = d
End Property
Now you can access properties using myinstance.IntData!MyProperty = 1... but to get to where you want to be you need to use Charlie Pearson's technique for making IntData the default member for your class.
Once that's done, you can use the following syntax:
Dim m As MyClass
Set m = New MyClass
Debug.Print "Age = " & m!Age ' prints: Age =
m!Age = 27
Debug.Print "Age = " & m!Age ' prints: Age = 27
Set m = Nothing
Okay, thanks to Alain and KyleNZ I have now found a working way to do this, without having a collection or enumerable object below.
Basically, thanks to the name of the ! operator, I found out, that access via the bang/pling operator is equivalent to accessing the default member of an object. If the property Value is the default member of my class module, then there are three equivalent statements to access that property:
obj.Value("param")
obj("param")
obj!param
So to make a short syntax working for a custom class module, all one has to do is to define a default member. For example, I now used the following Value property:
Property Get Value(name As String) As String
Value = SomeLookupInMyXMLDocument(name)
End Property
Property Let Value(name As String, val As String) As String
SetSomeNodeValueInMyXMLDocument(name, val)
End Property
Normally, you could now access that like this:
obj.Value("foo") = "New value"
MsgBox obj.Value("foo")
Now to make that property the default member, you have to add a line to the Property definition:
Attribute Value.VB_UserMemId = 0
So, I end up with this:
Property Get Value(name As String) As String
Attribute Value.VB_UserMemId = 0
Value = SomeLookupInMyXMLDocument(name)
End Property
Property Let Value(name As String, val As String) As String
Attribute Value.VB_UserMemId = 0
SetSomeNodeValueInMyXMLDocument(name, val)
End Property
And after that, this works and equivalent to the code shown above:
obj("foo") = "New value"
MsgBox obj("foo")
' As well as
obj!foo = "New value"
MsgBox obj!foo
' Or for more complex `name` entries (i.e. with invalid identifier symbols)
obj![foo] = "New value"
MsgBox obj![foo]
Note that you have to add the Attribute Value.VB_UserMemId = 0 in some other editor than the VBA editor that ships with Microsoft Office, as that one hides Attribute directives for some reason.. You can easily export the module, open it in notepad, add the directives, and import it back in the VBA editor. As long as you don't change too much with the default member, the directive should not be removed (just make sure you check from time to time in an external editor).
See this other question: Bang Notation and Dot Notation in VBA and MS-Access
The bang operator (!) is shorthand for
accessing members of a Collection or
other enumerable object
If you make your class extend the Collection class in VBA then you should be able to take advantage of those operators. In the following question is an example of a user who extended the collection class:
Extend Collections Class VBA