why (ClassType as TMyClass).Create fails

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

why (ClassType as TMyClass).Create fails

Dennis
Following up to the cloning example code:

Type
  TMyClassRef = class of TMyClass;

implementation

function TMyClass.Clone(AOwner: TComponent): TMyClass;
begin
   Result := TMyClassRef(ClassType).Create(AOwner);

//but the next line will fail to compile
//  Result := (ClassType as TMyClassRef).Create(aOwner);
   Result.Assign(Self);
end

Isn't (ClassType as TMyClassRef).Create(aOwner)  supposed to be safer
because sometimes we copy and paste code around and in the end  
(ClassType might not be a descendant of TMyClass and the compiler won't
catch this type mismatch if we do

   Result := TMyClassRef(ClassType).Create(AOwner);

Am I missing something?

Dennis
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: why (ClassType as TMyClass).Create fails

Michael Van Canneyt


On Tue, 21 Jun 2016, Dennis wrote:

> Following up to the cloning example code:
>
> Type
> TMyClassRef = class of TMyClass;
>
> implementation
>
> function TMyClass.Clone(AOwner: TComponent): TMyClass;
> begin
>  Result := TMyClassRef(ClassType).Create(AOwner);
>
> //but the next line will fail to compile
> //  Result := (ClassType as TMyClassRef).Create(aOwner);

This should be
   TMyClassRef(ClassType).Create(aOwner);

>  Result.Assign(Self);
> end
>
> Isn't (ClassType as TMyClassRef).Create(aOwner)  supposed to be safer because
> sometimes we copy and paste code around and in the end  (ClassType might not
> be a descendant of TMyClass and the compiler won't catch this type mismatch
> if we do
>
>  Result := TMyClassRef(ClassType).Create(AOwner);
>
> Am I missing something?

No, this construct is not supported.

Michael.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: why (ClassType as TMyClass).Create fails

Michalis Kamburelis-3
In reply to this post by Dennis
 2016-06-21 15:57 GMT+02:00 Dennis <[hidden email]>:

> Following up to the cloning example code:
>
> Type
>  TMyClassRef = class of TMyClass;
>
> implementation
>
> function TMyClass.Clone(AOwner: TComponent): TMyClass;
> begin
>   Result := TMyClassRef(ClassType).Create(AOwner);
>
> //but the next line will fail to compile
> //  Result := (ClassType as TMyClassRef).Create(aOwner);
>   Result.Assign(Self);
> end
>
> Isn't (ClassType as TMyClassRef).Create(aOwner)  supposed to be safer
> because sometimes we copy and paste code around and in the end  (ClassType
> might not be a descendant of TMyClass and the compiler won't catch this type
> mismatch if we do
>
>   Result := TMyClassRef(ClassType).Create(AOwner);
>
> Am I missing something?
>

The "is" / "as" operators are just not defined on class references
(they only on classes and interfaces). Not really sure why.

To make the typecast *somewhat* safer (but see disclaimers below) you
can check the InheritsFrom (
http://www.freepascal.org/docs-html/rtl/system/tobject.inheritsfrom.html
) before casting. InheritsFrom is a class method, it can be used on
instances of class references easily.

So like

if ClassType.InheritsFrom(TMyClass) then
  Result := TMyClassRef(ClassType).Create(AOwner) else
  raise Exception.Create(...);

Note that you check for "InheritsFrom(TMyClass)" , not
"InheritsFrom(TMyClassRef)". So it will *not* secure from bugs when
someone changes the definition of TMyClassRef (to be "class of
TSomeOtherClass").

But it will secure from bugs if this code is copy-pasted into some
unrelated class, where ClassType is not compatible with TMyClass.

Actually, since this is inside an instance, you can write
"InheritsFrom(TMyClass)" instead of
"ClassType.InheritsFrom(TMyClass)". Actually you can write "Self is
TMyClass". Which is actually trivially true in this code (this is
inside a TMyClass method)... But maybe some variation of this check
will be useful, if this is used in a more complicated code, with
something less obvious than "ClassType" as an argument.

Regards,
Michalis
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal