How to Get a value of a property at runtime via reflection?

As the title Shows, am i able in Oxygene Island, to retrieve a value of, for instance, a class, or record?

typeOf(T).Properties[0].Value //i know, wrong Syntax, but like such, i am missing currently, because the propertyinfo variable has no "value" property, in C# there was a "value" property

This isn’t supported yet.

ok, do you plan it, to Support it on the Island platform?

Yes, there are plans.

Ok, could i get a value of a property maybe on a different way?

AFAIK:

Pascal/FPC had a way, which was the DFM, where you could create a dictionary and fill the values of the needed class in the dictionary and then populate it.

Does oxygene provide a Thing like that?

Not yet. As I said, we’re planning this feature, but it’s not ready yet. For simple properties (no parameters) and simple types, you could use reflection to get the method pointer (For the read accessor of a property). Then cast that to a function pointer like this:

type 
  ObjectGetterMethod = function(aSelf: Object): Object;
  IntegerGetterMethod = function(aSelf: Object): Integer;

if it's an integer:
IntegerGetterMethod(ProcPtr)(aInstance) will give you the result

if it's an Object:
ObjectGetterMethod(ProcPtr)(aInstance) will give you the result

Etc.

First, thanks for the suggestion :slight_smile:

But what exactly do u mean by: simple properties, with no Parameters?^^

But isnt integer not an object?

No index properties.

You need a different function pointer for every distinct value type result.

1 Like

Hmm :confused:

Unfortunately, this dont work, at least in my example…

  var text := 'hello world';
  var proc_ptr := text.GetType().Properties.FirstOrDefault().ReadMethod;
  var getter: Read_Method := Read_Method(proc_ptr);
  getter(text.Length);  //i also tried getter(text) and this didnt work as well

P.s The aim of this example is to get the “LENGTH” Property^^

This works on any custom class (really only String didn’t have proper rrti for this), next build the route through Properties works too:

namespace ConsoleApplication401;

type
  Read_Method = public function(aSelf: Object): Integer;
  MyString = public class 
  private 
    fs: String;
  public
    constructor(s: string); begin fs := s; end;
    property Length: Integer read fs.Length;
  end;
  Program = class
  public
  
    class method Main(args: array of String): Int32;
    begin  
      var text := new MyString('hello world');
      var proc_ptr := text.GetType().Methods.FirstOrDefault(a -> a.Name ='get_Length');
      var rm: Read_Method := Read_Method(proc_ptr.Pointer);
      
      writeln(rm(text));
      // add your own code here
      writeLn('The magic happens here.');
    end;
    
  end;

end.
1 Like

Thanks for the help carlo :slight_smile:

It works pretty fine :slight_smile:

In Addition to that, how works this with fields??

from records for instance, where i dont have always properties?

Thanks!

Trickier…
you can use the InstanceOffset property of FieldInfo to find the offset of the field relative to the record. then it kind of depends on how you have it. Say you have it boxed in an Object, it would be something along the lines:

^Integer(InternalCalls.Cast(myobject) + fieldInfo.InstanceOffset + if acontainingtype.IsValueType then sizeof(Pointer) else 0)

(to get an integer field)

Ok, that seams more ugly :smiley: :smiley:

But, honestly, i dont understand the line: "if acontainingtype.IsValueType then sizeof(Pointer) else 0) " because i dont know where the “acontainingtype.IsValueType” and the “Pointer” came from^^

sizeof(Pointer) should have been sizeof(^Void) (which is either 4 or 8)

what’s happening is this, InstanceOffset always gives the offset of an instance field as it would be stored in memory. For classes there’s always the Vmt first, then the first field, then the second etc. This is taken in account, for value types there is no vmt at all, so the first field has instance offset 0. However a function that would return a field from a struct would probably take an Object parameter, which means it has a boxed struct; those DO have a vmt prepended. so you have to add sizeof(^void) to it.

1 Like

THANKS!!

Works pretty fine :slight_smile:

But the last Thing i dont really get is, why do i Need the “sizeof(^Void)” when i know for 100% it is an pure record/valuetype and it is NOT boxed?

I tested it before without that line and the Output was: “?” and when i added it, the Output of my record was fine, have a look:

  var data : TSomeData;
  data.A := 'D';      
  data.B := '2';
  data.C := '3';
  data.D := '4';
  data.E := 177;

  var fieldInfo := data.GetType().Fields.FirstOrDefault();      

  var value := ^Char(InternalCalls.Cast(data) + fieldInfo.InstanceOffset + if data.GetType().IsValueType then sizeof(^Void) else 0); 
   // add your own code here
  writeLn(value^);
  readLn;

In this case @data + instancefieldoffset will do

and what would be the underlying result type of it?

I ask this because the Compiler say: "Cannot cast “method pointer” to object.

^String(@data + fieldInfo.InstanceOffset)^ or something like it (depends on the types of Tsomedata?)

Hmm, can u have a look :confused: ?

 var rec : TRec;
  rec.X := 10;
  rec.Y := 20;
  rec.Z := 30;


  var d := ^Int32(@rec + rec.GetType().Fields.FirstOrDefault().InstanceOffset)^; // Error: "Cannot cast from method pointer to ^int32"
  writeLn(d);
  readLn;

This should do:

namespace ConsoleApplication5;

type
TRec = record public X, Y, Z: Integer; end;
  Program = class
  public
  
    class method Main(args: array of String): Int32;
    begin
      // add your own code here
        var rec : TRec;
        rec.X := 10;
        rec.Y := 20;
        rec.Z := 30;
        for each el in  rec.GetType().Fields do begin 
            writeLn('off: '+el.InstanceOffset+' '+el.Name);
            var d := ^Int32(IntPtr(@rec) + el.InstanceOffset)^; // Error: "Cannot cast from method pointer to ^int32"
            writeLn(d);
        end;
        readLn;
    end;
    
  end;

end.

but i just noticed InstanceOffset is wrong for the 2nd and 3rd field, so I’ll be taking a look at that.