0

I have the following class, from which I would like to get some names from the members, such as names

TInterface = interface(IXMLNode)
  function Get_One: Boolean;
  function Get_Two: Boolean;
  function Get_Three: Boolean;
  procedure Set_One(Value: Boolean);
  procedure Set_Two(Value: Boolean);
  procedure Set_Three(Value: Boolean);
  property One: Boolean read Get_One write Set_One;
  property Two: Boolean read Get_Two write Set_Two;
  property Three: Boolean read Get_Three write Set_Three;
end;

TTesting = class(TXMLNode, TInterface)
protected
  function Get_One: Boolean;
  function Get_Two: Boolean;
  function Get_Three: Boolean;
  procedure Set_One(Value: Boolean);
  procedure Set_Two(Value: Boolean);
  procedure Set_Three(Value: Boolean);
end;

to get the name I use the following method that uses TRttiContext

procedure getName();
var
  Ctx: TRttiContext;
  PropList: TArray<TRttiProperty>;
begin
  PropList := Ctx.GetType(TTesting).GetProperties;

 for i:= 0 to Length(PropList) do S1:= PropList[i].name; PropList[i].ToString;
.......

when run it, I always have 1 item called RefCount.

Should not I get the values 'One' 'Two' 'Three' ?

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Btw, it's normal to name an interface with an `I` prefix, not `T`. T is for classes and records. – David Apr 16 '15 at 17:18
  • There's also this: http://stackoverflow.com/questions/8679735/how-do-i-enumerate-all-properties-in-an-object-and-obtain-their-values – David Schwartz Apr 17 '15 at 16:10

2 Answers2

1

Your class does not contain any properties beyond those that it inherits from TXMLNode. Specifically it inherits the public property TInterfacedObject.RefCount.

Whilst it implements an interface that contains properties, the class itself does not contain properties. Here is the class definition:

type
  TTesting = class(TXMLNode, TInterface)
  protected
    function Get_One: Boolean;
    function Get_Two: Boolean;
    function Get_Three: Boolean;
    procedure Set_One(Value: Boolean);
    procedure Set_Two(Value: Boolean);
    procedure Set_Three(Value: Boolean);
  end;

As you can clearly see, there are no properties declared there.

Some other comments:

  • By default, RTTI is not produced for protected members. You need to specify using $RTTI EXPLICIT that you want RTTI for protected members, as recently discussed here: Call a protected method (constructor) via RTTI. Were you to add a property to your class, you may need to use $RTTI EXPLICIT PROPERTIES to be able to reflect it.
  • Your loop for i := 0 to Length(PropList) runs off the end of the array. Loop up to Length(...) - 1 or to high(...).
Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Also, RTTI is not generated for properties declared in interfaces, as the compiler does not actually implement real properties in interfaces like it does for classes. Interface properties are just language syntax shortcuts for conveniently calling the interface's methods instead. – Remy Lebeau Apr 17 '15 at 00:27
0

There are multiple problems here. Notwithstanding the details in the other answer, even if the class had the properties defined correctly, the loop is improperly constructed:

for i:= 0 to Length(PropList) do S1:= PropList[i].name; PropList[i].ToString;

This runs through all of the items plus one, but ultimately leaves ONLY ONE item assigned to S1 (the last one), which is what you're complaining about originally.

Now, the PropList[i] that's assigned happens to be past the end of the list and should generate a compiler warning that i is used after the end of the loop, as well as generate a likely run-time exception.

You didn't define S1 anywhere, either, and it's not clear what it's even doing there.

I note that you posted this same erroneous loop code in the earlier post's comment, and it wasn't addressed.

I'd show the correct code, but I'm not sure what exactly you're trying to accomplish, so I'll leave that for you to figure out on your own. But it should look more like this:

for i:= 0 to Length(PropList) do 
begin
  S1:= PropList[i].name; // where is S1 being used?
  PropList[i].ToString;  // <-- this isn't doing anything
end;
David Schwartz
  • 1,756
  • 13
  • 18
  • I think I covered this already in my answer. What's really happening is that the caller isn't using that loop, and the loop is really just fake code made up for the question. – David Heffernan Apr 17 '15 at 06:13
  • Yes, but given the way that line was written, I'm thinking the OP didn't realize the missing begin/end mattered. When it's shown correctly, it looks more obvious that it's wrong. – David Schwartz Apr 17 '15 at 16:14