Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   as (https://www.delphipraxis.net/171895-as.html)

Popov 30. Nov 2012 16:58

as
 
Ich bin leicht verwirrt. Natürlich kenne ich
Delphi-Quellcode:
as
und auch den Unterschied zwischen
Delphi-Quellcode:
TTest(abc)
und
Delphi-Quellcode:
(abc as TTest)
. Vor allem die Laufzeitprüfung und die nette Fehlermeldung. Ich gebe offen zu, dass ich
Delphi-Quellcode:
as
sehr selten nutze, da ich von meiner Einstellung her keine Überraschungen mag und somit auch keine Fehlermeldungen, ausgenommen meine. Das Objekt wird bei mir also zuerst überprüft und evtl. eine Alternative gewählt. Eine nette Fehlermeldung im Programm bringt mir nichts.

Trotzdem, ich hab gerade viel Code studiert und etliche Klassen und bin etwas verwirrt was den Mix angeht der vorherrscht. Einfach zu behaupten, dass wenn man sich sicher ist was kommt, dann
Delphi-Quellcode:
TTest(abc)
, wenn man sich überraschen will und eine nette Fehlermeldung haben will, dann
Delphi-Quellcode:
(abc as TTest)
. Guckt man sich so manchen Code an wird mal das eine mal das andere genommen. Den Sinn sehe ich oft nicht.

Wenn ich dann sowas sehe (nur mal als ein Beispiel von etlichen die mir heute aufgefallen sind), dann frage ich mich, hat es einen tieferen Sinn oder war der Programmierer an beiden Tagen lediglich unterschiedlich drauf. In der Regel gehe ich davon aus, dass ich nur den Sinn nicht erkenne

Delphi-Quellcode:
function TAbc.Get(Index: Integer): TGraphic;
begin
  Result := TGraphic(inherited Get(Index));
end;

procedure TAbc.Add(Item: TGraphic);
begin
  inherited Add(Item);
end;

function TDef.Get(Index: Integer): TBitmap;
begin
  Result := inherited Get(Index) as TBitmap;
end;

procedure TDef.Add(Item: TBitmap);
begin
  inherited Add(Item);
end;

Furtbichler 30. Nov 2012 17:03

AW: as
 
Es soll auch so etwas wie schlechte Programmierung geben.

Ich würde nur in einem Fall casten:

Delphi-Quellcode:
If MyInstance is TSomeObject then
  TSomeObject(MyInstance).DoSomeThing;
In allen anderen Fällen würde ich assen :mrgreen:. Auch wenn es irgendwie klar ist, das das mit dem typecasting schon hinkommt, ist es dennoch nicht 'sicher', denn es kann theoretisch passieren, das die Typen eben nicht kompatibel sind.

stahli 30. Nov 2012 17:51

AW: as
 
Man sollte natürlich immer sicher stellen, dass nur auf korrekte Typen gecastet wird.

if O is Txyz ... ist also nie verkehrt, um auch andere Fälle explizit zu behanden, bzw. ein falsches casten auszuschließen.
Aber auch nach bestandener Prüfung würde ich mit "as" casten. Das finde ich übersichtlicher, obwohl es dann eigentlich unnötig ist.

Wenn man ausschließen kann, dass falsche Klassen übergeben werden, dann kann man auch ohne vorherige Prüfung auch hart casten.
Ich würde aber dennoch immer "as" verwenden. Wenn sich doch mal ein Fehler einschleicht, erhält man wenigstens eine Fehlermeldung.

In neueren Versionen bieten sich in vielen Fällen natürlich alternativ die Generics an.

himitsu 30. Nov 2012 18:11

AW: as
 
Zitat:

dass ich as sehr selten nutze, da ich von meiner Einstellung her keine Überraschungen mag und somit auch keine Fehlermeldungen
Da ist deine Einstellung genau falschrum, in Bezug auf deine Fehlererwartung.

AS wirf nur eine Exception, wenn sich die Objektinstanz nicht casten läßt, da das Objekt nicht dem dem angegebenen Typen entspricht, bzw. kein Nachfahre davon ist.

Und genau da ist diese Exception vollkommen in Ordnung.


Viel schlimmer wäre es, wenn du eine inkompatibles Objekt hart castes, was dann eventuell erst irgendwo später Exceptions wirft, z.B. wenn man auf etwas nicht existierendes, oder schlimmer noch auf etwas Falsches zugreift.
Oder ganz gemein, wenn der Zugriff keinen Fehler sofort wirft, aber natürlich auch nicht das macht, was man wollte ... da könnte ein Fehler oder vieleicht eine Exception erst viel später auftreten, was dann oftmals fast garnicht mehr nachvollziehbar ist.

Dort hast du es nämlich wesentlich schwerer, die (eigentliche) Ursache zu finden.

Also wenn Cast, dann gibt es nur zwei/drei mögliche Wege:
- man weiß 100,1 %-ig, daß wirklich immer nur dieser Typ ankommt, dann kann man bedenkenlos hart casten
- man kann nicht sicher sein, dann muß es geprüft werden

- weiß man den Typ nur nicht und will entscheidend reagieren, dann vorher mit IS prüfen und eine Auswahl treffen (danach kann gerne hart gekastet werden, da IS ja sicher geprüft hat)

- will/muß man auf etwas zugreifen, dann muß der Typ 100%ig sicher sein, dann womit man dort mit AS castet. (außer man hat vorher mit IS geprüft)
- wenn man mehrfach auf das Objekt zugreift, dann kann man vorher mit IS prüfen und wenn nicht, dann eine Exception werfen oder eine Assertion einsetzen (welches sich im Release deaktivieren läßt, wenn man den Code 100%ig auf Korrektheit geprüft hat)



PS:
IS ist eine fehlerlose Prüfung, welche einen Boolean zurückgibt, wenn das keine kompatible Instanz des gewünschten Typs ist.
AS ist ein prüfender Cast, welcher den gecastete Instanz-Zeiger zurückgibt und intern "theoretisch" ein IS aufruft.
obj.InheritsFrom entspricht quasi einem IS, auér daß dort natürlich eine Instanz (kein NIL) vorhanden sein muß, da man ja sonst die Methode nicht aufrufen kann.
(OK, man hätte das NIL bei Letzerem theoretisch im Code abfangen können, aber es gibt an der Stelle wenistens eine verständliche Zugriffsverletzung durch Lesen von Adresse $00000000)

Aber:
IS ist nur erfolgreich (True), wenn in der Variable ein Objekt enthalten ist (nicht NIL) und wenn es sich bei der Instanz um den gewünschten Typen oder einen Nachfahren handelt.
AS dagegen läßt auch NIL erfolgreich durch.

Also theoretisch müßte man beim AS auch noch mit Assigned prüfen,
aber praktisch ist es nicht so schlimm, wie es klingt, denn ein NIL erzeugt beim Zugriff eine (zuverlässige) definierte Fehlermeldung (siehe InheritsFrom), welche sich problemlos zurückverfolgen kann, da man nur von der Fehlerstelle zurückgehen muß, bis dahin von wo der Wert (nil) gekommen ist.


Delphi-Quellcode:
var
  Obj: TObject;
  X, Y: TList;
begin
  Obj := nil;

  if Obj is TList then
    ShowMessage('jupp');

  try
    if Obj.InheritsFrom(TList) then
      ShowMessage('jo');
  except
    on E: Exception do
      ShowMessage(E.ClassName + ': ' + E.Message);
  end;

  Y := TList(Obj);
  X := Obj as TList;

  if X = Y then ; // damit der Compiler nix wegoptimiert
end;


Ach ja, wenn man ganz paranoid ist, dann darf man gerne alle eingehenden Parameter prüfen,
aber da Delphi eine recht strikte Typenprüfung besitzt, darf man gerne davon ausgehen, daß ein Parameter/Variable/Feld/... mindestens diesem Typen entspricht oder nil ist.
Falls doch irgendwo ein Idiot absichtlich, bzw. grob fahrlässig, diese Typenprüfung umgeht und ein total inkompatibles Objekt dort reinhackt, dann ist das halt so und und man darf es beruhigt unkontroliert knallen lassen.

Soll heißen: Wenn man eine Variable TList hat, dann ist dort einfach immer nur mindestens eine TList (oder Nachfahre) oder Nichts (nil) drinnen
und Falls doch mal ein TEdit da reingeraten ist ... Pech.

Popov 30. Nov 2012 19:02

AW: as
 
Zitat:

Zitat von Furtbichler (Beitrag 1193758)
Ich würde nur in einem Fall casten:
...
In allen anderen Fällen würde ich assen

Das entspricht eigentlich auch meiner Vorgehensweise. Ich prüfe alles stets zuerst, somit weiß ich womit ich arbeite. Wozu dann also as wenn ich die Klasse kenne.

Wobei wenn ich mir jetzt mein Beispiel angucke, dann ist es evtl. nicht glücklich gewählt. Ich hab mich zu stark von - zwei gleiche Aktionen, zwei Vorgehensweisen - einnehmen lassen. Durch den Zugriff auf die Vorgängerklasse gibt man die Kontrolle aus der Hand. Nun ja, wenn man TBitmap rein stopft, sollte eigentlich auch TBitmap raus kommen. Aber ok, man bekommt etwas von einer anderen Funktion geliefert. In diesem besonderen Fall ist der Fall eigentlich klar. Schlechtes Beispiel.

Mal sehen, vielleicht finde ich bessere Beispiele.

Zitat:

Zitat von himitsu (Beitrag 1193766)
Da ist deine Einstellung genau falschrum, in Bezug auf deine Fehlererwartung.

Also ich weiß nicht ob meine Einstellung falsch ist, vielleicht haben wir nur unterschiedliche Vorstellungen von den Ausgangsvoraussetzungen. Ok, das Beispiel ist vielleicht nicht glücklich gewählt, aber was bringt dem Nutzer meines Programms eine schöne Fehlermeldung. Besser ist es gar nicht erst so weit kommen zu lassen, wenn es geht.

Aber wie bereits gesagt, denn Unterschied kenne ich. Nur gehe ich bei solchen Beispielen wie oben zuerst davon aus, dass es schon seine Richtigkeit hat.


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:57 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