Folks, Does Clarion support the concept of CASTing in OOP? In C# I might declare a Base Class and use that for my Derived classes but store only references to the Base Class and then cast the data to access methods etc at runtime. Or does Clarion to this implictly?
Alas, I donât know C# well enough to make a direct comparison, but here is what Clarion does (so hopefully this answers your question).
Letâs say I have a class
StringTheory
And I use this class as a pointer or parameter
bruce procedure(StringTheory pStr)
other &StringTheory
code
Then I can pass in any class derived from StringTheory as the parameter, or assign to the pointer.
BigBang Class(StringTheory)
Explosion Procedure()
End
code
Bruce(BigBang)
The only limitation is that the Bruce function cannot access the new methods or any new properties, because they are not part of StringTheory. So;
bruce procedure(StringTheory pStr)
other &StringTheory
x long
code
x = pStr.records() ! legal
pStr.Explosion() ! not legal
Thanks Bruce, but not quite what I was thinking.
In your example above, you would need to tell the compiler that you want to treat your pStr as a BigBang not a StringTheory. But you have told the procedure to accept a StringTheory so I understand why ! Not Legal would be correct.
In C# (or C++) you could create for example a queue of ANIMALS. You donât care in the queue what sort of animals they are but they are of the base type ANIMALS.
During runtime I could iterate over that list of ANIMALS and do something like:
MyAnimal = GET(ANIMALS) ! Get next Animal from the QUEUE
If MyAnimal IS A DOG then (DOG)MyAnimal.Woof
If MyAnimal IS A CAT then (CAT)MyAnimal.Weow
CAT and DOG are derived ANIMALS and the (DOG) or (CAT) casts the MyAnimal to make the compiler that it as a DOG or CAT object/class.
The reason for this is that you can store different types of things in a queue or list but as they are all just ANIMALS (because that is the base class) you can treat them as ANIMALS or CAST them to what they really should be.
From what I can see, Clarion doesnât support this. It supports ANY but then again I canât cast an ANY object to the type of Object I want it to be.
I guess what I could do is (thinking out loud) and assuming that the ANIMALS base class had a IsA property then could I:
I_AM_A_DOG PROCEDURE(DOG pDog)
I_AM_A_CAT PROCEDURE(CAT pCAT)
MyAnimal = Get(ANIMALS)
If MyAnimal.IsA() = âDOGâ then I_AM_A_DOG(MyAnimal)
Would this then be treated as a DOG in the I_AM_A_DOG class or would the compiler not like me passing an ANIMALS to something that wanted a DOG?? ??? Need to go and try this.
No you canât CAST in Clarion like in C#. Use virtual methods instead.
Another approach of âcastingâ (I donât recommend this though):
Test PROCEDURE(TAnimal pAnimal)
thisDog &TDog
thisCat &TCat
CODE
CASE UPPER(pAnimal.GetName())
OF 'DOG'
thisDog &= ADDRESS(pAnimal)
thisDog.Woof()
OF 'CAT'
thisCat &= ADDRESS(pAnimal)
thisCat.Weow()
ELSE
MESSAGE('Unknown animal!')
END
As @Mike_Duglas pointed out, there is a way to UPCAST in clarion, but it relies on you finding a way to implement your own IS A logic.
This sort of coding will usually lead to maintenance headaches, which is to say they are a bad idea ( a code smell ). I too would encourage you to find a different way to accomplish what youâre writing.
Also as pointed out by @Bruce you can easily DOWNCAST in clarion, however that too has some nuance. If you call a method on the downcast object, then derived methods are not called unless they are ,VIRTUAL in the downcast object. This holds true for the DESTRUCT method, so disposing of a downcast object can lead to memory leaks.
It didnât seem 100% clear to me that what Mark was saying is making Destruct VIRTUAL prevents this leak of stuff the Derived class needs to Dispose() so Destruct should always be VIRTUAL.
Say you had a Truck Class derived from Vehicle that needed itâs own Queue which is Disposed in the Destructor:
Truck CLASS(Vehicle) , TYPE
CONSTRUCT Procedure() ! New(TruckQueue) Truck specific data
DESTRUCT Procedure() , VIRTUAL ! Dispose(TruckQueue) Truck specific data
TruckQueue &TruckQueueType
END
Typical code only references the Truck class so a DISPOSE(Truck) will fire the Truck DESTRUCT without Virtual.
The problem comes if your code references the Truck as the Vehicle base class. If you Dispose that Vehicle Reference the Truck DESTRUCT will not fire without VIRTUAL. A way Iâve seen this is a Queue of &VehicleClass references that can be any Derived type like a Truck. To cleanup each Queue record class reference is disposed and they are &VehicleClass references so need Virtual to call the Truck.Destruct().
I learnt that one the hard way.