Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Usando RTTI para devolver lista de eventos (https://www.clubdelphi.com/foros/showthread.php?t=90415)

elrayo76 02-06-2016 04:24:12

Usando RTTI para devolver lista de eventos
 
Intentando usar RTTI, no puedo encontrar una forma que me devuelva una lista con todos los eventos que pueda tener una clase.

He mirado las units del propio entorno de Delphi (XE8) y no hay nada que se de forma directa. Lo que he encontrado que dentro de la unit System.RTTI existe la clase TRttiType que contiene los métodos GetMethods, GetFields, GetProperties, GetIndexedProperties entre otros.

Mi pregunta ahora es la siguiente. ¿Puede ser que GetMethods devuelva todos los métodos de una clase, incluidos los que se usan para los evntos. Si es asi, como identifico cuales son los de los eventos, cuales son procedimientos, funciones, constructor, etc.?

Como dije antes, en la unit correspondiente no he visto nada que me de una idea de como hacer lo que quiero. ¿Acaso tendre que identificar cada uno de ellos por lo que diga delante (procedure, function, constructor, etc)?

Si alguien sabe como hacerlo y tiene algún ejemplo que me pueda orientar se los agradecería mucho. Ya saben cualquier cosa que algo no se entienda pueden preguntar y sera aclarado.

Saludos,
El Rayo

AgustinOrtu 02-06-2016 04:58:11

Al parecer, los eventos en Delphi son identificados mediante TTypeKind.tkMethod

Entonces, podes hacer esto:

Código Delphi [-]
uses
  Rtti;

procedure TForm1.Button1Click(Sender: TObject);
var
  c: TRttiContext;
  &Type: TRttiType;
  Prop: TRttiProperty;
begin
  Memo1.Clear;
  &Type := c.GetType(TButton);
  for Prop in &Type.GetProperties do
  begin
    if Prop.PropertyType.TypeKind = TTypeKind.tkMethod then
      Memo1.Lines.Add(Prop.ToString);
  end;
end;

Salida:

Código Delphi [-]
property OnClick: TNotifyEvent
property OnContextPopup: TContextPopupEvent
property OnDragDrop: TDragDropEvent
property OnDragOver: TDragOverEvent
property OnDropDownClick: TNotifyEvent
property OnEndDock: TEndDragEvent
property OnEndDrag: TEndDragEvent
property OnEnter: TNotifyEvent
property OnExit: TNotifyEvent
property OnKeyDown: TKeyEvent
property OnKeyPress: TKeyPressEvent
property OnKeyUp: TKeyEvent
property OnMouseActivate: TMouseActivateEvent
property OnMouseDown: TMouseEvent
property OnMouseEnter: TNotifyEvent
property OnMouseLeave: TNotifyEvent
property OnMouseMove: TMouseMoveEvent
property OnMouseUp: TMouseEvent
property OnStartDock: TStartDockEvent
property OnStartDrag: TStartDragEvent
property OnDropDownClick: TNotifyEvent
property WindowProc: TWndMethod
property OnGesture: TGestureEvent

ecfisa 02-06-2016 10:47:24

Hola.

Otra opción que les servirá también a los que disponen de versiones mas viejitas:
Código Delphi [-]
uses TypInfo;

procedure GetMethodNames(AComponent: TComponentClass; TS: TStrings);
var
  i   : Integer;
  pInf: PPropList;
begin
  for i := 0 to GetPropList(AComponent.ClassInfo, pInf)-1 do
    if pInf[i].PropType^.Kind = tkMethod then
      TS.Add(Format('%s: %s',[pInf[i].Name, pInf[i].PropType^.Name]));
end;

// Ej. de uso
...
begin
   GetMethodNames(TListBox, ListBox1.Items); 
   ...

Saludos :)

elrayo76 02-06-2016 17:04:32

Primero que nada, gracias por las respuestas. En cuanto pueda vere las dos opciones que me dan, ya que esto me servirá para hacer que el Framework pueda hacer lo mismo en versiones nuevas y mas viejas.

Saludos,
El Rayo

AgustinOrtu 02-06-2016 19:24:23

Hola Daniel, acabo de probar tu ejemplo en Delphi 2010 y funciona bien

Simplemente no entiendo porque limitas la clase a inspeccionar a que sea heredero de TComponent

Segun las pruebas que he hecho, funciona bien si heredamos de TObject:


Código Delphi [-]
  TCustomEvent = procedure of object;

  TMyClass = class
  private
    FEvento: TNotifyEvent;
    FEventoCustom: TCustomEvent;
    procedure SetEvento(const Value: TNotifyEvent);
    procedure SetEventoCustom(const Value: TCustomEvent);
  published
    property Evento: TNotifyEvent read FEvento write SetEvento;
    property EventoCustom: TCustomEvent read FEventoCustom write SetEventoCustom;
  end;

implementation

uses
  TypInfo;

procedure GetMethodNames(AClass: TClass; TS: TStrings);
var
  I: Integer;
  pInf: PPropList;
begin
  for I := 0 to GetPropList(AClass.ClassInfo, pInf) - 1 do
    if pInf[i].PropType^.Kind = tkMethod then
      TS.Add(Format('%s: %s', [pInf[i].Name, pInf[i].PropType^.Name]));
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  TS: TStrings;
begin
  TS := TStringList.Create;
  try
    GetMethodNames(TMyClass, TS);
    ShowMessage(TS.Text);
  finally  
    TS.Free;
  end;
end;

En este caso imprime tanto el TNotifyEvent como el TCustomEvent. Eso si, fue necesario declarar las propiedades como publicadas

roman 02-06-2016 19:54:48

Cita:

Empezado por AgustinOrtu (Mensaje 505880)
Simplemente no entiendo porque limitas la clase a inspeccionar a que sea heredero de TComponent

De hecho podría limitarlo a TPersistent, pero no más arriba, al menos en Delphi 7. O, mejor dicho, habría que verificar que AClass.ClassInfo no sea nil, y no lo será siempre que la directiva {$M+} esté presente, cosa que sucede con TPersistent.

Tú ejemplo, da un Access Violation en D7 hasta que se activa la directiva. No sé si en D 2010 se active siempre.

LnieComment Saludos

AgustinOrtu 02-06-2016 20:01:51

Ahh buen dato roman, no estaba al tanto de esos detalles

Como elrayo habla de un framework lo primero que se me ocurrió es que quizá exigir pertenecer a la rama de TComponent como algo no deseable; de seguro el ya tiene armada sus jerarquías.

Aún así, es bueno saber que con agregar la directiva {M+} funciona bien. Una solución bastante sencilla podría ser incluir ese switch para las clase ancestro del framework

ecfisa 03-06-2016 02:19:34

Hola.

En Delphi 7 no le veo mucho sentido remontar mas allá de TComponent por que por ejemplo el código:
Código Delphi [-]
{$M-}  // (también funciona) 

procedure GetMethodNames(AClass: TClass; TS: TStrings);
var
  I: Integer;
  pInf: PPropList;
begin
  if not Assigned(AClass) then Exit;
  for I := 0 to GetPropList(AClass.ClassInfo, pInf) - 1 do
    if pInf[i].PropType^.Kind = tkMethod then
      TS.Add(Format('%s: %s', [pInf[i].Name, pInf[i].PropType^.Name]));
end;
no generará error si envío como argumento un objeto auxiliar:
Código Delphi [-]
   GetMethodNames(TStringList, Memo1.Lines);
pero tampoco mostrará los eventos OnChange y OnChanging de la clase enviada.

En este momento no imagino el caso, pero tal vez exista algún caso para el que convenga declarar el parámetro de tipo TPersistent.

Saludos :)

roman 03-06-2016 04:09:40

Cita:

Empezado por ecfisa (Mensaje 505891)
no generará error si envío como argumento un objeto auxiliar:

No genera error porque aun cuando pones {$M-}, le pasas una clase que fué compilada con la directiva {$M+} y por tanto generó RTTI, y si pones {$M+} entes de la rutina de todas formas fallará si le pasas un clase compilada con {$M-}. Es decir, el valor de la directiva no cambiará de último momento lo que ya haya sido compilado.

Definitivamente, una rutina como la que escribiste necesita comprobar que ClassInfo no sea nil.

Ahora bien, si sólo vas a trabajar con clases de la VCL, ciertamente no tiene caso ir más allá de TComponent, pero no sabemos qué clase de framework esté trabajando y es posible que defina su propia jerarquía de clases independiente de la VCL.

LineComment Saludos

ecfisa 03-06-2016 04:53:54

Cita:

Definitivamente, una rutina como la que escribiste necesita comprobar que ClassInfo no sea nil.
Si el parámetro es declarado de tipo TClass, claramente es así.

Cita:

Ahora bien, si sólo vas a trabajar con clases de la VCL, ciertamente no tiene caso ir más allá de TComponent
Correcto.
Cita:

, pero no sabemos qué clase de framework esté trabajando y es posible que defina su propia jerarquía de clases independiente de la VCL.
Pero... ¿ funciona RTTI sobre una jerarquía propia (que no descienda de TObject) en Delphi 7 ?, te pregunto por que ciertamente lo desconozco.

Saludos :)

roman 03-06-2016 05:14:56

Cita:

Empezado por ecfisa (Mensaje 505898)
Pero... ¿ funciona RTTI sobre una jerarquía propia (que no descienda de TObject) en Delphi 7 ?

Todo desciende de TObject. Pero puedes tener jerarquías que no tengan nada que ver con TPersistent -la clase base de la VCL- y que estén compiladas con {$M+} y generarán RTTI.

LineComment Saludos

ecfisa 03-06-2016 05:58:02

Ahora entendí a que te referías, había comprendido otra cosa por jerarquía propia (de ahí mi sorpresa y pregunta)

Saludos :)

Al González 03-06-2016 07:10:48

Si siguen así, voy a volver a participar como en los viejos tiempos, intentando aportar con tan buen ánimo como ustedes. ¡Qué gusto es leerles! :)

roman 03-06-2016 08:34:57

Resurjamos como el ave Fénix :)

LineComment Saludos


La franja horaria es GMT +2. Ahora son las 15:59:47.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi