Some questions re using class vars

This forum is meant for questions and discussions about the X# language and tools
Post Reply
FFF
Posts: 1568
Joined: Fri Sep 25, 2015 4:52 pm
Location: Germany

Some questions re using class vars

Post by FFF »

I have a SLE subclass, for capsuling some "special" behaviour. One of that is simply the sle's background color. Now, there are datawindows with dozens of SLEs, so,
after Chris set me on track re class vars, i realized that i might initialize brush and colorobject once as class var in the owning window, which later on the the sle instances might use. In constructor something like

Code: Select all

CLASS dta_Konzerte INHERIT pdta_Konzerte
STATIC oOldBrush, oPause AS Brush
STATIC oC AS Color

CONSTRUCTOR(oWindow,iCtlID,oServer,uExtra)
...
SUPER(oWindow,iCtlID,oServer,uExtra)
..
dta_Konzerte.oC := Color{COLORYELLOW}
dta_Konzerte.oPause:= Brush{oC}
Return
1) for housekeeping, i added

Code: Select all

METHOD DESTRUCTOR()
dta_Konzerte.oC:= NULL_OBJECT
dta_Konzerte.oPause:=NULL_OBJECT
SUPER:Destroy()
RETURN
Is it safe to assume, the super:destroy does the same as the non existent super:destructor?

2) in the constructor of the sle i set

Code: Select all

SELF:Background:=dta_Konzerte.oPause
which compiles, (why sees the sle the class var at all, there's no warning ), but at runtime no background is set, why?

Code: Select all

SELF:Background:= Brush{Color{COLORYELLOW}}
instead works.

Alternatively it put this in a show() of the sle, but that doesn't help, too.

3) ultimately, the background should depend on the textvalue of the sle, probably the check should be put into the window's notify or better in the editfocuschange?

Sorry for stupid questions, but i finally decided to put away with my shame, to learn something...
Regards
Karl
(on Win8.1/64, Xide32 2.20, X#2.20.0.3)
User avatar
Chris
Posts: 4826
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Re: Some questions re using class vars

Post by Chris »

Hi Karl,

When you have a STATIC member of a class, this means that there's only one instance of it, no matter how many times you instantiate the class. On the other hand, the (regular) constructor gets called every time you instantiate a new window, so if you put code that creates new objects and assign them to static class members, those will be recreated over and over again. And because the destructor also gets called for every instance of the window, nullifying/destroying them there means they will be destroyed for all windows, even for those that are already open!

So instead you need to instantiate those objects in a (extra) STATIC CONSTRUCTOR(), which gets called only once for the lifetime of the application, so you will only create them once. And there's no need to destroy them ever, they live as long as the application lives and will be released automatically when the application exits.

All that is of course if all your windows need to use the exact same objects. If instead every window needs to use different colors/brushes etc, then you'd need to use regular instance fields (class members) to hold them and indeed destroy them on window close. If it's something in between, for example there are 4-5 possible brushes and each window might use any one of them, then you can (and it's the best way to) still store them all in separate STATIC members and use them as needed in each window.

Btw, NULLifying an object like a brush is not doing much, it only tells the GC that it is not used anymore (unless it is also assigned somewhere else), so it can be eventually collected and destroyed. If you need to release everything needed by a brush or similar object, you need to directly call its Destroy() method instead.

Also no need to call Destroy() from a Window subclass destructor. All of the destructors defined in a class hierarchy are called automatically (and you cannot call manually a SUPER:Destructor()) and the destructor of the most base class (VObject) does call Destroy() itself. You would need to call Destroy() in your own defined classes, if it's not being called already by a destructor in a parent class.
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
FFF
Posts: 1568
Joined: Fri Sep 25, 2015 4:52 pm
Location: Germany

Re: Some questions re using class vars

Post by FFF »

Hi Chris,
thx for explaining. As you assumed, it's a "some brushes" case. Added the static constructor and setting the sle background in the sle constructor works.

For conditional setting, depending on the value, i'm obviously still on the wrong track, as it doesn't work. I understand, that in constructor there's no value to check, but i'd think that in a sle:show something like

Code: Select all

	IF SELF:Value ="00000"
		SELF:Background:=dta_Konzerte.oPause
	ENDIF
should do at least for initial display?

Re the destructors: My upbringing was, "if you add a equally named method in a subclass to change behaviour, you have to call at the end it's parent, to avoid missing necessary handling." If i don't have to do so here, the better...
Regards
Karl
(on Win8.1/64, Xide32 2.20, X#2.20.0.3)
User avatar
Chris
Posts: 4826
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Re: Some questions re using class vars

Post by Chris »

Hi Karl,

Ah I see the confusion, METHOD Destroy() and DESTRUCTOR() are actually two completely different things, although they are often used for similar reasons.

A method called Destroy() is generally named like that (in the VOSDK classes) only as a convention (in your own set of classes you could name it "ReleaseUsedResources()" for example instead) and it is a regular method like all other methods. And like all other methods, from within a METHOD Destroy(), you need to call SUPER:Destroy(), for the same named methods to be executed up in the class hierarchy.

Btw, the system libraries in the .Net framework use a different name as a convention, they use "Dispose()", which is intended for the same thing as Destroy() is used in the VOSDK, to do clean up when an object is no longer needed.

A destructor is a different thing, it is a special method that gets executed automatically by the .Net runtime, when an object gets collected by the Garbage Collector and its memory used is freed. And all destructors in the class hierarchy are called (semi-)automatically, you cannot manually call a base destructor (as you can do with constructors). You can do anything you want in a destructor (for example logging about when objects get collected by the GC), but typically destructors are used for freeing resources, by calling another method, which can be Destroy(), Dispose(), ReleaseUsedResources() etc.

The reason why you do not typically release resources inside a destructor directly, but instead use a different regular method, is to be able to call this method manually. Destructors are called only when the GC collects their objects, and this can take a while, or never happen at all, until the app exits. While on the other hand, when you do not need an object anymore, you can call Destroy()/Dispose() on it to immediately release its external resources, close file handles etc and not wait for the GC to do it. In fact the object may be still "alive" for the lifetime of the app (if for example there's a variable always pointing to it, so the GC will never collect it), but it's not usable anymore (unless you tell it to re-allocate the resources it needs!).

To get into more detail, destructors in .Net are actually emitted by the compiler(s) in the exe/dll files as methods, with a special name "Finalize()". And the compiler automatically adds code in every Finalize() method to (among other things) call super:Finalize(), that's how the automatic invocation of all destructors happens under the hood, but that's an implementation detail. It's more or less like the "METHOD Axit()" mechanism that VO has.

Regarding the specific case with the SLE, I suspect the value of the SLE is not the one that you expect when you try to change the color, or your new method does not get executed at all when you expect it for some reason. Try logging the value of the SLE somewhere (in a file, in the console, with System.Diagnostics.Debug.WriteLine() or similar) and see if that's indeed the case.
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
User avatar
Chris
Posts: 4826
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Re: Some questions re using class vars

Post by Chris »

One more thing, I noticed that there's code in the VOSDK for the BitmapObject class, that does what you also used in your code sample:

Code: Select all

CLASS BitmapObject INHERIT ShapeObject
	PROTECT oBitmap AS Bitmap
DESTRUCTOR()
	IF !InCollect()
		oBitmap := NULL_OBJECT
	ENDIF
	SUPER:Destroy()
This is wrong code, because it means that if the BitmapObject class itself has a Destroy() method, this will not get called (by this destructor at least), instead the parent Destroy() method will be called. Fortunately it's the only class in the SDK that I see is doing this, and also coincidentally it is not harmful, as in fact there's no Destroy() method defined in that level in the class hierarchy, but nevertheless it's bad code and will adjust it.
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
FFF
Posts: 1568
Joined: Fri Sep 25, 2015 4:52 pm
Location: Germany

Re: Some questions re using class vars

Post by FFF »

Chris,
thx for the concise explanation, really helpful - maybe Wolfgang might copy it to save in his knowlegdebase site?

Re the sle case: My problem is, that i don't manage to get to the code, where the check might happen. Ie, my Show() seems to be never called at all. A breakpoint isn't reached, some logoutput is not written to console.

I wonder, if adapting the backgound of a control, depending on its value, is such an exotic wish?
Regards
Karl
(on Win8.1/64, Xide32 2.20, X#2.20.0.3)
User avatar
Chris
Posts: 4826
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Re: Some questions re using class vars

Post by Chris »

Hi Karl,

I just realized you have put the code in the Show() method of the control, not of the window one. The Show() method is not a callback, it's a method used to show the control if it's hidden, and the VOGUI classes do not call this when initially puttin gthe control on a window, it's in visible state by default. You'd need to explicitly call Show(), for the code to be executed.

But why don't you simply put this code in the constructor of the control?
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
FFF
Posts: 1568
Joined: Fri Sep 25, 2015 4:52 pm
Location: Germany

Re: Some questions re using class vars

Post by FFF »

Because evaluating the condition doesn't work. At that moment the Control:Value is still Nil.
Regards
Karl
(on Win8.1/64, Xide32 2.20, X#2.20.0.3)
User avatar
robert
Posts: 4465
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

Re: Some questions re using class vars

Post by robert »

Karl,
Is the SLE of your own type?
In that case you could override the TextValue assign and intercept the changes, or route that to the window that owns the control.
Something like:

Code: Select all

INTERFACE ITextValueChanged
    METHOD OnTextValueChanged(oC as Control, cText as STRING) as void
END INTERFACE

CLASS MySle INHERIT SingleLineEdit
.
.

ASSIGN TextValue (value) 
SUPER:TextValue := value
IF SELF:Owner IS iTextValueChanged VAR oMyOwner
   oMyOwner:OnTextValueChanged(SELF, value)
ENDIF
RETURN
END CLASS

CLASS MyDialogWindow ... IMPLEMENTS ITextValueChanged
.
.
METHOD OnTextValueChanged(oControl as Control, cTextValue as string) as void
// your code here
RETURN
END CLASS

CLASS MyDataWindow ... IMPLEMENTS ITextValueChanged
.
.
METHOD OnTextValueChanged(oControl as Control, cTextValue as string) as void
// your code here
RETURN
END CLASS


Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
Post Reply