Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Rätselhaftes Konstrukt in Unit Dialogs (https://www.delphipraxis.net/166090-raetselhaftes-konstrukt-unit-dialogs.html)

idefix2 29. Jan 2012 22:09

Rätselhaftes Konstrukt in Unit Dialogs
 
Hallo, auf der Suche nach einer etwas flexibleren Routine als ShowMessage und MessageDlg bin ich zwar im Quelltext der Unit Dialogs fündig geworden, da gibt es z.B. messageDlgPos, bei dem man die Position des Dialogs am Bildschirm festlegen kann, und überladene Routinen, denen man noch den Titel der Messagebox mitgeben kann - ich bin aber dabei über ein Delphi konstrukt gestolpert, das ich noch nie gesehen habe, und würde gerne wissen, was das eigentlich ist:

Delphi-Quellcode:
[UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.SafeSubWindows)]
function MessageDlgPos(const Msg: string; DlgType: TMsgDlgType;
  Buttons: TMsgDlgButtons; HelpCtx: Longint; X, Y: Integer): Integer;
begin
  Result := MessageDlgPosHelp(Msg, DlgType, Buttons, HelpCtx, X, Y, '');
end;

[UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.SafeSubWindows)]
function MessageDlgPos(const Msg: string; DlgType: TMsgDlgType;
  Buttons: TMsgDlgButtons; HelpCtx: Longint; X, Y: Integer;
  DefaultButton: TMsgDlgBtn): Integer; overload;
begin
  Result := MessageDlgPosHelp(Msg, DlgType, Buttons, HelpCtx, X, Y, '', DefaultButton);
end;
Was bedeutet jeweils die Zeile vor der Funktionsdeklaration mit den eckigen Klammern?

Luckie 29. Jan 2012 22:26

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Was das ist, kann ich dir auch nicht sagen. Würde mich aber auch interessieren. Aber hast du schon mal an die Windows API Funktion Messagebox gedacht oder den VLC Wrapper als Methode von TApplication: Applicatiuon.Messagebox? Bzw. was sollte der Dialog denn können?

idefix2 29. Jan 2012 22:43

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Eigentlich genügt mir das, was ich in der Unit Dialogs gefunden habe: Die Messagebox beliebig am Bildschirm positionieren und ihr einen Titel verpassen - Diesbezüglich bin ich ganz glücklich.

Aber ich habe noch nie in einem Delphi-Programm eine Zeile gesehen, die so in eckigen Klammern herumschwirrt, und hätte gerne gewusst, was das soll - einfach nur interesse halber.

Bernhard Geyer 29. Jan 2012 22:49

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Meinst du UIPermission? Dürfte sich um Überbleibsel der gescheiterten VCL.NET-Implementierung handeln. Damit teilst du mit welche Rechte die Methode/Funktion benötigt.

idefix2 29. Jan 2012 22:53

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
das ist schon einmal eine interessante information - aber die Syntax ist mir trotzdem rätselhaft: nach allem was ich bis jetzt an Delphi Syntax kenne müsste der Compiler doch bei den eckigen Klammern streiken???

himitsu 29. Jan 2012 22:56

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Das sind "Attribute".

Such einfach mal in der Unit RTTI nach "Attribute" oder schau dich beim DataSnap um (aktuelles Beispiel in der Nutzung)
Auch wenn ich dieses vollkommen krank finde, denn wer sollte schon auf die blöde Idee kommen Gruppen und Passwörter Hardgecodes im Queltext anzulegen, wo doch alle mehr mit dynamischen Strukturen arbeitet, wie z.B. Datenbanken. Oder kommt das wirklich so cool, wenn man die Anwendung neu kompilieren soll, nur um eine neue Rolle anzulegen oder ein Passwort zu ändern? :wall:
http://docwiki.embarcadero.com/RADSt..._Autorisierung

Dort kann man also zusätzliche Infos zu Funktionen/Prozeduren, Klassen/Typen, Methoden, Propertys und Variablen hinterlegen, welche sich dann via RTTI auslesen lassen.
Das wurde auch an ein/zwei Stellen in den Delphi-Quellcodes angewendet. (aber ich weiß nicht mehr wo ich das gesehn hatte und Aufgrund der Syntax läßt es sich nicht grade leicht suchen)

Delphi-Quellcode:
type
  TMyAttribute = class(TCustomAttribute)
    constructor Create(S: string);
  end;

  [TMyAttribute('7')]
  TMySet = set of TMyEnum;

  ['direkt als Text']
  TMyEnum = type TMyEnum;

  [TMyAttribute('1')]
  TMyClass = class
    [TMyAttribute('8')]
    FMyField: Integer;
    [TMyAttribute('2')]
    [TMyAttribute('3')]
    procedure MyMethod2;
    [TMyAttribute('2'), TMyAttribute('3')]
    function MyMethod: Boolean;
    [TMyAttribute('4')]
    property MyProperty: Boolean read MyMethod;
  end;

var
  [TMyAttribute('5')]
  MyVar: Integer;

[TMyAttribute('6')]
procedure MyProcdure;
Und nun kann man sich die Klassen und eventuelle Parameter via RTTI auslesen/verarbeiten.

idefix2 29. Jan 2012 23:09

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Danke - wieder einiges dazugelernt :)

jaenicke 30. Jan 2012 04:43

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Beispiel für die Nutzung bei uns:
Per Attribut wird festgelegt welche XML-Knoten oder Datenbankeinträge zu den Feldern der Klasse gehören. Danach kann man die einfach an eine Funktion übergeben, die dann die Daten liest oder schreibt. Benutzen tun wir das z.B. bei Einstellungsklassen. Auf diese Weise müssen die Einstellungsklassen nichts über die Datenstrukturen wissen, in denen die Daten liegen.

Uwe Raabe 30. Jan 2012 08:21

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Zitat:

Zitat von jaenicke (Beitrag 1148195)
Beispiel für die Nutzung bei uns:
Per Attribut wird festgelegt welche XML-Knoten oder Datenbankeinträge zu den Feldern der Klasse gehören. Danach kann man die einfach an eine Funktion übergeben, die dann die Daten liest oder schreibt. Benutzen tun wir das z.B. bei Einstellungsklassen. Auf diese Weise müssen die Einstellungsklassen nichts über die Datenstrukturen wissen, in denen die Daten liegen.

Mache ich genauso - als hätten wir voneinander abgeschrieben :-D "Great minds think alike!"

Luckie 30. Jan 2012 08:24

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Könnt ihr da mal ein beispiel zeigen?

daywalker9 30. Jan 2012 08:53

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Zum beispiel sowas:

Delphi-Quellcode:
type
  ListAttribute = class(TCustomAttribute)
  private
    FTagName: String;
    FClass: TClass;
  public
    constructor Create(const ATagName: String; AClassType: TClass);
   end;


type
  TMyClass = class
    [ ListAttribute('SubPositions', TPositionInformation) ]
    property SubPositions : TObjectList<TPositionInformation> read FSubPositions write FSubPositions;
    [ ListAttribute('DatabaseVersions', TDatabaseVersion) ]
    property DatabaseVersions : TObjectList<TDatabaseVersion> read FDatabaseVersions write FDatabaseVersions;
    [ ListAttribute('ProductionUnits', TProductionUnit) ]
    property ProductionUnits : TObjectList<TProductionUnit> read FProductionUnits write FProductionUnits;
    [ ListAttribute('Segments', TPositionSegment) ]
    property Segments : TObjectList<TPositionSegment> read fSegments write FSegments;
   end;
Passend dafür gibts einen Serializer der auf der neuen RTTI aufbaut und wenn ich eine Liste habe, weiß ich durch das Attribute, wie der Knoten in der XML heißt und welche Klasse ich dafür erzeugen muss.

s.h.a.r.k 30. Jan 2012 09:04

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1148211)
Zitat:

Zitat von jaenicke (Beitrag 1148195)
Beispiel für die Nutzung bei uns:
Per Attribut wird festgelegt welche XML-Knoten oder Datenbankeinträge zu den Feldern der Klasse gehören. Danach kann man die einfach an eine Funktion übergeben, die dann die Daten liest oder schreibt. Benutzen tun wir das z.B. bei Einstellungsklassen. Auf diese Weise müssen die Einstellungsklassen nichts über die Datenstrukturen wissen, in denen die Daten liegen.

Mache ich genauso - als hätten wir voneinander abgeschrieben :-D "Great minds think alike!"

Ich verklage euch -- ihr habt mein Patent 0815 verletzt :mrgreen:

@Luckie: Wie scheinbar auch andere, handhabe ich das Mapping von Datenbank-Tabellen auf Objekte via Attribute. Beispiel:
Delphi-Quellcode:
// Tablle Blub:
//   Spalte1
//   Spalte2
//   Spalte3

TModel = class abstract
public
  // Diese Methode analysiert die Klasse und lädt aus der zugewiesenen
  // Tabelle den nötigen Datensatz
  procedure Load(const PrimaryKey: Integer); virtual;
end;

// Dazu gehörige Klasse
[TTableAttribute('Blub')] // Zuweisung einer Tabelle
TBlubModel = class(TModel)
private
  [TTableColumnAttribute('Spalte1')]
  FValue1: String;
  [TTableColumnAttribute('Spalte1')]
  FValue2: Integer;
  [TTableColumnAttribute('Spalte1')]
  FXyz: Variant;
end;
So könnte das ganze aussehen. Du brauchst somit quasi nur eine Load-Methode schreiben, die via (der neuen) RTTI die abgeleitete Klasse TBlub analysiert und dann via PrimaryKey entsprechend lädt. Ist halt eine stark simplifizierte Version, aber ich denke, dass es schon aussagekräftig genug ist, bzw. das will ich mal hoffen ;)

-- Edit: Alternativ könnte man auch einen "Loader" verwenden -- bin in Sache Patterns bzgl sowas leider nicht all zu fit. Hier mal mein Gedankengang:
Delphi-Quellcode:
// Es liegt hier selbige Tabelle, wie oben beschrieben, zugrunde!

// Basis-Model-Klasse
TModel = class abstract
end;

TModelClass = class of TModel;

// Konkretes Model
TBlubModel = class(TModel)
private
  [TTableColumnAttribute('Spalte1')]
  FValue1: String;
  [TTableColumnAttribute('Spalte1')]
  FValue2: Integer;
  [TTableColumnAttribute('Spalte1')]
  FXyz: Variant;
end;

// Loader
TBaseModelFactory = class
private
  procedure AnalyseModel(Model: TModelClass);
public
  function LoadByPk(Model: TModelClass; PrimaryKey: Integer): TModel;
  function LoadByAttributes(Model: TModelClass; Attributes: TArray<TModelAttribute>): TArray<Model>;
  function LoadbyWhereClause(Model: TModelClass; WhereClause: String): TArray<TModel>;
end;

// Nutzung:
var
  Model : TBlubModel;
  Models : TArray<TBlubModel>;
begin
  // Laden, über die Angabe des PK. Durch Angabe der klasse TBlubModel ist klar,
  // auf welche Tabelle zugegriffen werden muss.
  Model := TBaseModelFactory.LoadByPk(TBlubModel, 10);

  // Laden via "Filter", d.h. über Angabe von Attributen, die dann entsprechend
  // zu einer Where-Clause zusammengebaut werden.
  // WICHTIG: Die Attribute sind hier nicht in korrekter Delphi-Syntax angegeben
  // sondern entsprechend Pseudocode.
  Models := TBaseModelFactory.LoadbyAttributes(TBlubModel, ['Spalte1' = 10, 'Spalte2' = 'DP']);

  // ... und dann noch via selbst modelliertem Where-Clause.
  Models := TBaseModelFactory.LoadByWhereClause(TBlubModel, 'Spalte1 > 15 AND Spalte2 LIKE "%dp%"');
Für etwaige Fehler wird nicht gehaftet :stupid: habe gerade alles nur schnell aus dem Kopf runtergeschrieben, ohne es zu testen.

DeddyH 30. Jan 2012 09:13

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
IIRC macht Microsoft das ähnlich, wenn man z.B. in VS ein Dataset zu einer bestehenden Tabelle erstellen lässt.

himitsu 30. Jan 2012 09:19

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Delphi-Quellcode:
type
  TMyClass = class
    [ ListAttribute('SubPositions', TPositionInformation) ]
    property SubPositions : TObjectList<TPositionInformation> read FSubPositions write FSubPositions;
    [ ListAttribute('DatabaseVersions', TDatabaseVersion) ]
    property DatabaseVersions : TObjectList<TDatabaseVersion> read FDatabaseVersions write FDatabaseVersions;
    [ ListAttribute('ProductionUnits', TProductionUnit) ]
    property ProductionUnits : TObjectList<TProductionUnit> read FProductionUnits write FProductionUnits;
    [ ListAttribute('Segments', TPositionSegment) ]
    property Segments : TObjectList<TPositionSegment> read fSegments write FSegments;
   end;
Wobei man hier uch wieder von doppelten Informationen reden könnte.
TagName entpsricht ja dem Property-Namen, so daß man diesen Namen doch auch direkt verwenden könnte?
OK, an die Klasse in dem Generic ranzukommen ist nicht so einfach, aber Möglich wäre es bestimmt auch. :stupid:

sakura 30. Jan 2012 09:19

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Diese Attribute sind insbesondere spannend, wenn man sich aus der DB-Struktur komplette Quellcodes erzeugen lässt. Dann fügt man die Attribute hinzu, um später dynamisch auf allen erzeugten Klassen zugreifen zu können, ohne dass man diese alle untereinander verlinken muss.

Delphi-Quellcode:
type
  /// <summary>
  /// Definitionen für das DB-Schema [inject]
  /// </summary>
  [DefineSchema('inject', '{12543F95-68E1-4F42-BA5D-479AFBBA12E1}')]
  TSchemaInject = class(TSchemaDefinition)
  public
    type
      /// <summary>
      /// Definitionen für die DB-SProc [inject].[Base_Login_Write]
      /// </summary>
      [DefineStoredProc(TSchemaInject, 'Base_Login_Write')]
      TSProcBase_Login_Write = class(TStoredProcDefinition)
      public
        ...
Später kann man im Code diese Klassen alle wieder auffinden und nutzen:
Delphi-Quellcode:
  Ctx := TRttiContext.Create;
  try
    for Typ in Ctx.GetTypes do
    begin
      if Typ.TypeKind <> tkClass then
        Continue;

      // load schemata
      if not Typ.AsInstance.MetaclassType.InheritsFrom(TSchemaDefinition) then
        Continue;

      for Attr in Typ.GetAttributes do
      begin
        if not (Attr is DefineSchemaAttribute) then
          Continue;
        FKnownSchemata.Add(TSchemaDefinitionClass(Typ.AsInstance.MetaclassType));
        Break;
      end;
    end;
  finally
    Ctx.Free;
  end;
Aus meiner Sicht eine der besten Neuerungen in der Delphi-Language seit langer Zeit (zusammen mit den Generics)

...:cat:...

himitsu 30. Jan 2012 09:35

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Zitat:

Zitat von sakura (Beitrag 1148217)
Aus meiner Sicht eine der besten Neuerungen in der Delphi-Language seit langer Zeit (zusammen mit den Generics)

Das For-in nicht zu vergessen. :angle:

daywalker9 30. Jan 2012 13:41

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Zitat:

Zitat von himitsu (Beitrag 1148216)
Delphi-Quellcode:
type
  TMyClass = class
    [ ListAttribute('SubPositions', TPositionInformation) ]
    property SubPositions : TObjectList<TPositionInformation> read FSubPositions write FSubPositions;
    [ ListAttribute('DatabaseVersions', TDatabaseVersion) ]
    property DatabaseVersions : TObjectList<TDatabaseVersion> read FDatabaseVersions write FDatabaseVersions;
    [ ListAttribute('ProductionUnits', TProductionUnit) ]
    property ProductionUnits : TObjectList<TProductionUnit> read FProductionUnits write FProductionUnits;
    [ ListAttribute('Segments', TPositionSegment) ]
    property Segments : TObjectList<TPositionSegment> read fSegments write FSegments;
   end;
Wobei man hier uch wieder von doppelten Informationen reden könnte.
TagName entpsricht ja dem Property-Namen, so daß man diesen Namen doch auch direkt verwenden könnte?
OK, an die Klasse in dem Generic ranzukommen ist nicht so einfach, aber Möglich wäre es bestimmt auch. :stupid:

Das supported Delphi aktuell ja leider nicht :(

mjustin 30. Jan 2012 15:36

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Zitat:

Zitat von sakura (Beitrag 1148217)

Aus meiner Sicht eine der besten Neuerungen in der Delphi-Language seit langer Zeit (zusammen mit den Generics)

Schade nur, dass Attributdefinitionen nicht einschränken können, auf welche Codeteile das Attribut angewendet werden darf. Man kann ein Attribut daher fast überall dort hinsetzen, wo man auch einen Kommentar setzen könnte :)

Oder ist das bei XE2 eventuell ergänzt worden, dass so etwas wie @Scope ElementType.FIELD auch in Delphi geht:

Meta-Annotationen, Sichtbarkeit, annotierbare Programmelemente

himitsu 31. Jan 2012 09:06

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Überall?

Ich kenn viele Stellen, wo ich Kommentare machen kann, aber keine Attribute.


Das geht immer nur vor irgendwelchen Deklarationen, wozu es eine RTTI gibt und scheinbar auch bei Variablen und Prozeduren.
Auch wenn ich jetzt nicht wüßte, wie man an deren RTTI rankommt, bzw. daß sie überhaupt eine haben. Aber da die neue RTTI rießig ist und dort jeder mögliche Scheiß drinsteht, könnte ich auch glauben, daß sich dazu auch 'ne RTTI finden ließe. :wall:

mjustin 31. Jan 2012 09:40

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Zitat:

Zitat von himitsu (Beitrag 1148449)
Überall?

Ich kenn viele Stellen, wo ich Kommentare machen kann, aber keine Attribute.


Das geht immer nur vor irgendwelchen Deklarationen, wozu es eine RTTI gibt und scheinbar auch bei Variablen und Prozeduren.
Auch wenn ich jetzt nicht wüßte, wie man an deren RTTI rankommt, bzw. daß sie überhaupt eine haben. Aber da die neue RTTI rießig ist und dort jeder mögliche Scheiß drinsteht, könnte ich auch glauben, daß sich dazu auch 'ne RTTI finden ließe. :wall:

Ja, so war das auch gemeint - an sehr vielen Stellen können Attribute platziert werden (es ist leider nicht genau dokumentiert, man kann es aber durch Trial & Error herausfinden).

Hier ist eine Stackoverflow Frage und eine Aufstellung möglicher Einsatzorte:

Which language elements can be annotated using attributes language feature of Delphi?

Genannt werden:

Enums
Function type
Procedure type
Event type (closure, procedure of object)
Aliased type
Record type
Class type
Record type that's internal to a class
Record field
Record method
Class instance field
Class class field (class var)
Class method
Global variable
Global function
Local variable



Doch RTTI ist ja schon einen Schritt zu spät. Was ich meinte bezieht sich auf den Zeitpunkt des Compilierens, bevor RTTI erzeugt wird. Man kann in Delphi nicht angeben, dass ein selbst definiertes Attribut ausschliesslich an bestimmten Quelltextelementen wie Properties erlaubt ist - so dass Anwender des Attributes beim Versuch, es z.B. vor einer Klassendefinition oder einem Parameter zu verwenden, einen Compilerfehler erhält.

Der Zweck ist offensichtlich, falsche Verwendung eines Attributes zu verhindern. In Delphi kann man erst zur Laufzeit, per RTTI, erkennen ob ein Attribut auch da steht, wo es bestimmungsgemäß hingehört.

himitsu 31. Jan 2012 09:58

AW: Rätselhaftes Konstrukt in Unit Dialogs
 
Achso.


Dann wirst du dir einen PostCompile-Assistenten schreiben müssen, welche das nach dem Kopilieren in der EXE prüft und eine Fehlermeldung anzeigt.

Oder einen Pre-Compiler-Assistenten, wo du den Quelltext parst und es dort abprüfst.


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:59 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz