Monday, June 11, 2018

Class definition

In contrast to most languages, with Oxygene (and all other Pascal variants), a class is first defined and then implemented. Within the class definition you define the structure and interface of the class, and this is where you define the visibility of each method, property, field or constant. The compiler directives as inlining and locking are also defined here.
Properties, fields and constants are completely defined in the class definition, methods (including property implementation methods) are defined here, but implemented later in the implementation section.

Class Visibility
The most simple class definition you can write:
The code above defines the class MyClass that inherits from Object (a class alwaysI inherits from another class, if you don't specify the bas class, Object will be used as base class).
The visibility of the class is not defined and will be Unit (see Visibility modifiers).

We can make the class public accessible by adding the visibility modifier Public:

Class modifiers
With a class modifier you give a hint to the compiler of how to compile this class:
abstractabstract means that the class must be inherited before it can be used.When a class definition has abstract methods defined, the class must be abstract too.
partialA partial class definition is a class that can be defined over more than one code file. By example used for the designer code of a winforms definition.
readonlyWhen a class is marked readonly, all variables and properties are readonly. This means that they can only be set from within a constructor of the class.
sealedA sealed class can not be inherited.
staticA static class can not be instantiated and all methods and properties within the class are marked static. You can see this as a module from VB.Net.
extensionThe following text literally copied from the Oxygene website:
Defines an extension type, a type which extends another type on Cocoa, called a category on Cocoa.

Example of the usage of a class modifier:

Class inheritance
Oxygene uses a single inheritance model, where every class inherits from only one other class - multiple inheritance, like in C++, where one class can inherit from multiple base classes, is not supported.

With inheritance, the new class has all methods, properties, fields and constants available that are defined in the base class, as long as the visibility of those items is Protected or less (for assembly and protected, the inheritance must be done in the same project and for unit and protected, the inheritance must take place in the same file to be able to see those class items).
 To inherit your class from another class, you have to write:
With this statement you create the class MyClass that is a subclass of baseClass, and baseClass is the superclass of MyClass (that is why inheritance is also often referred to as subclassing).

Class constructors
When a class is instantiated, the constructor is executed. A constructor is a special kind of method that initializes the class (makes it ready for use) and is the only place where you can initialize read only fields.
A class can have more than one constructor - and can even have a parameter-less static constructor that is executed just before the first time that a static member of the class is called.

To define a constructor, you use the constructor keyword:
In the example above, two constructors are added to the class, one without and one with parameters. You can add as much constructors as you want / need to the class.
A constructor should always call the (a) constructor of the base class, to make sure that the inherited functionality is initialized properly. Constructors can call each other with the constructor keyword:

Class destructors or finalizers
When a class is being destroyed (GC or reference counting), the finalizer is executed to clean the state of the instance.
A finalizer is always executed from an unknown thread - so the code should be thread safe!
The code of the finalizer is a last resort to clean up - it should already been done before the finalizer executes; with the use of the dispose pattern or within the state handling of the class code.
A finalizer is defined with the Finalizer keyword:
A class can only have one finalizer and the finalizer is a method without any parameters. The implementation:

Extending the class
The newly created class can be extended by adding methods, properties, fields and constants to it. This is called extending because the class inherited all methods, properties fields and constants from its base class - and you can extend the class by adding more of them:
As an example; the code above extends the class with the public property NewProp - if the base class had a public property OldProp, this is still available in the newly created class MyClass.

Re-introduction and overriding
Existing methods and properties can be re-introduced or overridden with a new implementation - only methods and properties that are labeled with the virtual modifier in the base class can be overridden, all others can only be re-introduced.

So what is the difference between re-introduction and overriding? The difference between the two are small, but can have a big impact within the execution of your program.

With a re-introduction, the subclass defines a completely new method or property with the same name of a method or property in the superclass. This seems no problem, but when a class is cast to it's ancestor, the original method or property is called instead of the re-introduction.
An example:

When you call SomeMethod in baseClass, it returns 4 and when you call it in MyClass it returns 2. But when you cast MyClass to baseClass (or a parameter of a method is of type baseClass while you supply an instance of MyClass), the result will be 4.

If you override the method instead of re-introduce it:
Now, when you call SomeMethod in baseClass, it still returns 4 and when you call it in MyClass it still returns 2. But when you cast MyClass to baseClass (or a parameter of a method is of type baseClass while you supply an instance of MyClass), the result will be 2 instead of 4, meaning that the overridden method is used instead of the original.

The same is explained in the post Method modifiers.

Interface inheritance
Where a class can only inherit from one baseclass, it can inherit from multiple interfaces - all of those interfaces have to be implemented (unless the interface has a default implementation, in that case the interface implementation is not mandatory).
The synatx to inherit from an interface is exactly the same as the syntax to inherit from a base class. Multiple interfaces to inherit from are separated by comma's:
The example above inherits from the interface IDisposable.
To use the interface, we have to implement all methods and properties within the interface (in this case only the method Dispose):
When you implement more interfaces that have conflicting names, or you have reason to use another name than the standard name, you can use another name and the implements modifier to tell the compiler which interface is being implemented:
The implementation methods do not have to be public - they can also be private (or any other visibility), but as soon as you cast the object to the interface, the interface methods will be public and the methods will have their original names.

No comments:

Post a Comment