Jump to content

Recommended Posts

4 minutes ago, Ondrej Kelle said:

the second is a type declaration (class).

A declaration of an alias, anyway.  TMyClass and TAnotherClass will be the same type, as far as the compiler and RTTI are concerned.  Now, if you do this instead:

TMyClass = type TAnotherClass; 

Or this:

 TMyClass = class(TAnotherClass)

Then TMyClass will be its own unique type.

Share this post


Link to post
Guest
1 minute ago, Remy Lebeau said:

A declaration of an alias, anyway.  TMyClass and TAnotherClass will be the same type, as far as the compiler and RTTI are concerned.  Now, if you do this instead:


TMyClass = type TAnotherClass; 

Or this:


 TMyClass = class(TAnotherClass)

Then TMyClass will be its own unique type.

Thanks! I think you're completely right and off-topic to the question. 🙂

Share this post


Link to post

hmmmm ... so we have four "variations" of this  (maybe more):

 

1) TMyClass = TAnotherClass;

 

2) TMyClass = class of TAnotherClass;

 

3) TMyClass = type TAnotherClass;

 

4) TMyClass = class( TAnotherClass );

 

I'm familiar with (1) and (4), but (2) and (3) I've rarely seen used.

 

In what conditions would (2) or (3) be used?

Share this post


Link to post

They all do different things. Rather than have us explain this, did you read the documentation? 

 

I don't think that item 3 is very widely used. However meta classes, item 2, is widely used. Classic example is the streaming framework. It is used when you need to instantiate a class whose identity is only known at runtime. When streaming properties the framework reads the class name, looks it up using RTTI and then uses meta classes to instantiate the instance if that dynamically determined type. Always seen with virtual constructors. 

Edited by David Heffernan
  • Thanks 1

Share this post


Link to post
2 hours ago, David Schwartz said:

TMyClass = type TAnotherClass;

 

This treats TPassword like a string yet in Berlin+ you can add a unique Record Helper for TPassword.

TPassword = type string;

// Example of a Helper for TPassword
  TPasswordHelper = record helper for TPassword
    function CheckPwdComplexity(const MinPwdLen: Word; const Level: TPwdComplexity): boolean;
    function ContainsLower(const Required : Cardinal = 1): boolean;
    function ContainsSpecial(const Required : Cardinal = 1): boolean;
    function ContainsUpper(const Required : Cardinal = 1): boolean;
    function Secure(const ProtectWith: TSecureStringProtection = ssFast): ISecureString;
    function BCrypt: TBCrypt; inline;
  end;
  • Like 2
  • Thanks 1

Share this post


Link to post
2 hours ago, David Schwartz said:

but (2) and (3) I've rarely seen used

(2) is used pretty extensively. In the Delphi source folder (and subfolders) there are 386 instances of it.

Share this post


Link to post

Metaclasses are necessary when base Class1 is doing some things with Class2 or its descendants including creation. So you can either define virtual method CreateObj: TClass2 and override it (but that would require Class1 descendant) or just assign "class of TClass2" property.

  • Like 1

Share this post


Link to post

I personally use nr 2 really often. Imagine a simple encoder:

TBaseEncoder = Class [...]
TEncoderV1 = Class(TBaseEncoder) [...]
TEncoderV2 = Class(TBaseEncoder) [...]
TEncoderV3 = Class(TEncoderV2) [...]

Basically you have a bunch of encoders, containing new methods, improvements, etc. Now let's say you want your application to be backwards compatible and be able to use previous encoders. If you add:

TBaseEncoderClass = Class Of TBaseEncoder;

...and define your application as:

TMyApplication = Class
strict private
 _myencoder: TBaseEncoder;
public
 Constructor Create(inMyEncoderClass: TBaseEncoderClass); ReIntroduce;
End;

Constructor TMyApplication.Create(inMyEncoderClass: TBaseEncoderClass);
Begin
 inherited;
 _myencoder := inMyEncoderClass.Create;
End;

then you simply can call:

TMyApplication.Create(TBaseEncoder);

or

TMyApplication.Create(TEncoderV3);

to create and make your application to use the specified version of your encoder.

Edited by aehimself
  • Like 1

Share this post


Link to post

@aehimself Or using generics:

  TMyApplication2<T: TBaseEncoder> = class
  strict private
    _myencoder: TBaseEncoder;
  public
    constructor Create;
  end;

constructor TMyApplication2<T>.Create;
begin
  inherited Create;
  _myencoder := TBaseEncoderClass(T).Create;
end;

begin
  var MyApp1 := TMyApplication2<TBaseEncoder>.Create;
  Writeln(MyApp1.ClassName);
  var MyApp2 := TMyApplication2<TEncoderV3>.Create;
  Writeln(MyApp2.ClassName);
end.

 

Share this post


Link to post

@Kryvich I recently started to use generics myself so I guess I'm far away of harvesting their true potential. I like your solution, though; it's really elegant 🙂

Share this post


Link to post
1 hour ago, Kryvich said:

Or using generics:

Nice solution as well if you control creation of parent class.

For example, there could be a middleware class that contains HTTP server that has client classes defined. You can't control creation of HTTP server but can modify its props so if HTTPServer.HTTPClientClass is accessible you can extend client objects.

Share this post


Link to post

Of course HTTPServer should be "generalized" to let use it in such way.

Share this post


Link to post
On 2/13/2020 at 12:57 PM, Stefan Glienke said:

Using generics where class reference suffice is overkill and unnecessary bloat

Guilty.  Like OOP, Generics are sooo addictive!

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×