Loop properties from a class from a method in that class

Posted by DenDuze on 20-Sep-2019 13:00

Hi,

I want to loop all properties defined in a class from a method in that same class.
I also want that all the properties are private (so they can not be assigned a value from outside that class).

I found a way to loop those properties but my problem is that the properties needs to be public (and i do not want that).
Is there some way to loop all properties from a class (private/public)?

I tried with the following class: 
 /*------------------------------------------------------------------------
    File        : GetPropertiesFromClass
    Purpose     :
    Syntax      :
    Description :
    Author(s)   : didier.d
    Created     : Fri Sep 20 14:54:04 CEST 2019
    Notes       :
  ----------------------------------------------------------------------*/

using Progress.Lang.*.

block-level on error undo, throw.

class GetPropertiesFromClass:
  define private property pPrivateProperty as character no-undo
  get.
  set.
 
  define public property pPublicProperty as character no-undo
  get.
  set.
 
  define protected property pProtectedProperty as character no-undo
  get.
  set.

  /*------------------------------------------------------------------------------
   Purpose:
   Notes:
  ------------------------------------------------------------------------------*/
    
  constructor public GetPropertiesFromClass (  ):
    SUPER ().
    this-object:mLoopProperties().
  end constructor.

  /*------------------------------------------------------------------------------
   Purpose:
   Notes:
  ------------------------------------------------------------------------------*/

  method private void mLoopProperties(  ):
    define variable oProperty as Progress.Reflect.Property no-undo extent.
    define variable i         as integer                   no-undo.

    assign oProperty = cast(this-object, Progress.Lang.Object):Getclass():GetProperties().
    do i = 1 to extent(oProperty):
      message oProperty[i] skip oProperty[i]:name skip oProperty[i]:datatype
              view-as alert-box info buttons ok.
    end.
      
    return.

  end method.
 
 
 
  end class.

Regards
Didier

Posted by Lieven De Foor on 20-Sep-2019 13:44

[mention:60b86d3c60124129adc86cb35b53ac5b:e9ed411860ed4f2ba0265705b8793d05] , did you add "OR Flags:Instance OR Flags:DeclaredOnly" as well? These are needed to get the required properties.

This just gives you a Properties array. If you want to get the actual values, you need to loop over all items and call Get(Object) on them, where Object is your actual object instance of which you want to get the property values...

You are not calling the GetProperties() from "this class". You're calling it on the "Class" of the current instance of your object, which is not your object itself, hence you have no access to its private properties. Using reflection you can get them, if you provide the correct parameters to GetProperties().

Posted by Torben on 20-Sep-2019 13:56

GetProperties default to only public instance properties when not passing flags!

knowledgebase.progress.com/.../

All Replies

Posted by Torben on 20-Sep-2019 13:13

Hi Didier,

you can make the properties public with private setters.

/Torben

Posted by Lieven De Foor on 20-Sep-2019 13:15

- You can make a property read-only by making the setter private.

- You can pass Flags to the GetProperties method to only get what you want, e.g GetClass():GetProperties(Flags:Private OR Flags:Instance OR Flags:DeclaredOnly).

Posted by DenDuze on 20-Sep-2019 13:29

Hi,

Yes, damn forgot about that :-(

But otherwise, is this not possible

I find that strange because I call the method from this class so then the private properties should be available. or I'm a wrong here?

@Lieven: yes I know, I already tried with the private-flag but that also did not work

Posted by Lieven De Foor on 20-Sep-2019 13:44

[mention:60b86d3c60124129adc86cb35b53ac5b:e9ed411860ed4f2ba0265705b8793d05] , did you add "OR Flags:Instance OR Flags:DeclaredOnly" as well? These are needed to get the required properties.

This just gives you a Properties array. If you want to get the actual values, you need to loop over all items and call Get(Object) on them, where Object is your actual object instance of which you want to get the property values...

You are not calling the GetProperties() from "this class". You're calling it on the "Class" of the current instance of your object, which is not your object itself, hence you have no access to its private properties. Using reflection you can get them, if you provide the correct parameters to GetProperties().

Posted by Lieven De Foor on 20-Sep-2019 13:56

It appears you can't get the value of a private property using reflection, which is a bit strange (it can be done in other languages so why was this designed this way?)

USING Progress.Reflect.Flags FROM PROPATH.

USING Progress.Reflect.Property FROM PROPATH.

BLOCK-LEVEL ON ERROR UNDO, THROW.

CLASS TestReflection.TestReflection:

   DEFINE PRIVATE PROPERTY PrivateProperty AS CHARACTER NO-UNDO INITIAL "Hello!"

   GET.

   SET.

   METHOD PUBLIC VOID PrintPrivates():

       DEFINE VARIABLE Properties AS Property NO-UNDO EXTENT.

       Properties = GetClass():GetProperties(Flags:Private OR Flags:Instance OR Flags:DeclaredOnly).

       MESSAGE Properties[1]:Name SKIP

               Properties[1]:Get(THIS-OBJECT)

           VIEW-AS ALERT-BOX.

   END METHOD.

END CLASS.

Posted by Torben on 20-Sep-2019 13:56

GetProperties default to only public instance properties when not passing flags!

knowledgebase.progress.com/.../

Posted by DenDuze on 20-Sep-2019 13:57

Hi Lieven,

I do not completely understand what you wrote but I will test a bit

Thanks

Posted by Lieven De Foor on 20-Sep-2019 14:00

It appears you can't get the value of a private property through reflection, which is a weird design choice, since you can with Java or C#...

USING Progress.Reflect.Flags FROM PROPATH.

USING Progress.Reflect.Property FROM PROPATH.

BLOCK-LEVEL ON ERROR UNDO, THROW.

CLASS TestReflection.TestReflection:

   DEFINE PRIVATE PROPERTY PrivateProperty AS CHARACTER NO-UNDO INITIAL "Hello!"

   GET.

   SET.

   METHOD PUBLIC VOID PrintPrivates():

       DEFINE VARIABLE Properties AS Property NO-UNDO EXTENT.

       Properties = GetClass():GetProperties(Flags:Private OR Flags:Instance OR Flags:DeclaredOnly).

       MESSAGE Properties[1]:Name SKIP

               Properties[1]:Get(THIS-OBJECT)

           VIEW-AS ALERT-BOX.

   END METHOD.

END CLASS.

Posted by DenDuze on 20-Sep-2019 14:03

Thanks guys

Posted by Lieven De Foor on 20-Sep-2019 14:04

For some reason my answers don't get posted, so this is another (3rd) attempt...

It appears the value of private properties can't be read using reflection, which is a strange design choice, since this is possible in Java or c#...

Example code:

USING Progress.Reflect.Flags FROM PROPATH.
USING Progress.Reflect.Property FROM PROPATH.

BLOCK-LEVEL ON ERROR UNDO, THROW.

CLASS TestReflection.TestReflection:

DEFINE PRIVATE PROPERTY PrivateProperty AS CHARACTER NO-UNDO INITIAL "Hello!"
GET.
SET.

METHOD PUBLIC VOID PrintPrivates():

DEFINE VARIABLE Properties AS Property NO-UNDO EXTENT.

Properties = GetClass():GetProperties(Flags:Private OR Flags:Instance OR Flags:DeclaredOnly).

MESSAGE Properties[1]:Name SKIP
        Properties[1]:Get(THIS-OBJECT)
    VIEW-AS ALERT-BOX.

END METHOD.

END CLASS.

Posted by Shelley Chase on 20-Sep-2019 14:32

Hi Lieven,

I agree it was the wrong choice to not allow private access to reflection. We hope to add that capability soon.

Thanks

-Shelley

Posted by jquerijero on 20-Sep-2019 14:43

Flags:Private OR Flags:Instance OR Flags:DeclaredOnly - Is this OR in this case a bitwise operation? Is this a special case implementation, or we can start foregoing the use of the Progress helper class for bitwise operation?

Thanks,

Posted by Lieven De Foor on 20-Sep-2019 14:46

This is a bitwise operation.

You no longer need EnumHelper or the likes to do this kind of things since the more recent OE 11 versions...

Posted by Torben on 20-Sep-2019 16:41

But you can use dynamic property:

DYNAMIC-PROPERTY(THIS-OBJECT, Properties[1]:Name)

Posted by Lieven De Foor on 23-Sep-2019 07:01

@[mention:c4bc59ca912947f48646dd9e9ff4acd5:e9ed411860ed4f2ba0265705b8793d05] DYNAMIC-PROPERTY will only work from within the object. Trying to access a private property through DYNAMIC-PROPERTY from outside the class will give you error 13833:

Property PrivateProperty has a PRIVATE GET accessor so can be read only from within the class where it's defined. (13833)

Posted by Laura Stern on 23-Sep-2019 13:43

Lieven: I agree with what Shelley said.  However, in your TestReflection example, you are trying to access a private data member of the class that owns that variable.  This should be allowed now.  i.e., I believe this is a bug.  Otherwise, what would be the point in providing the Flags:Private enum if it will never allow you to get the property anyway!  If you can't access it from its own class, then where would you be able to access it from?  Please log a bug so we can look into this.

Posted by Evan Bleicher on 24-Sep-2019 15:31

As noted by Shelley and Laura the AVM’s reflection infrastructure can be enhanced to support accessing non-public data members via reflection.  From a historical perspective, when this feature was designed and built during the 11.6.0 time-frame we reviewed .NET’s and Java’s reflection capabilities.  These two frameworks approach this facility differently.  Via reflection, .NET provides functionality to access non-public data members and Java by default prohibits this access.  There are facilities within Java to mimic the .NET functionality.

The approach we identified at the time was to introduce a more restrictive solution in 11.6.0 (access to Public data member values only) with the possibility of relaxing this restriction in a future release.

One area of discussion was to better understand the use case for accessing non-public data members via reflection.  Can the Community comment on how they plan on using such a facility?

Posted by Lieven De Foor on 25-Sep-2019 07:59

Hi [mention:644b32c9259141d2b1b21a2813ab7487:e9ed411860ed4f2ba0265705b8793d05] I personally never had to access ABL private members through reflection (yet), but that's probably because we don't have 3rd party ABL without access to the source (and ability to adapt it). Accessing these through reflection would seem like a code smell to me in that case.

But I did have to use reflection on occasion to get a .NET private value (stackoverflow.com/.../how-to-get-the-real-value-of-the-visible-property).

This thread is closed