InstanceType
The InstanceType
type can be used as return type for class members to indicate that the member returns a value of the same (or more concrete) type as the instance it is being called on.
This allows for class hierarchies where more concrete descendants can override a method, and (be know to) return more concrete values that match their class. Consider the following example:
type
Employee = public class
public
property Name;
method Copy: Employee; virtual;
end;
Manager = public class(Employee)
public
property Underlings: List<Person>;
method Copy: Employee; override;
end;
public class Employee
{
public string Name { get; set; }
public virtual Employee Copy() { ... }
}
public class Manager : Employee
{
public List<Person> Underlings { get; set; }
public override Employee Copy() { ... }
}
public class Employee {
public var Name
public func Copy() -> Employee { ... }
}
public class Manager : Employee
{
public var Underlings: [Person]
public override func Copy() -> Employee { ... }
}
public class Employee
{
public string Name { __get; __set; }
public virtual Employee Copy() { ... }
}
public class Manager : Employee
{
public List<Person> Underlings { __get; __set; }
public override Employee Copy() { ... }
}
In this example of a class hierarchy, the Copy()
method can be used to create a copy of a given employee record. But, due to the nature of how traditional polymorphism works, calling Copy
on an instance of the more concrete Manager
class still returns the base class – requiring unnecessary casting:
var m := lMyManager.Copy();
var u := m.Underlings.Count; // fails
var m = lMyManager.Copy();
var u = m.Underlings.Count; // fails
let m = lMyManager.Copy()
let u = m.Underlings.Count // fails
var m = lMyManager.Copy();
var u = m.Underlings.Count; // fails
Here, we can't access the Underlings
property of the cloned Manager
, because even though we know that m
refers to a Manager
, the compiler only knows that m
is an Employee
.
Simply changing the return type of Copy
to InstanceType
fixes this:
type
Employee = public class
public
property Name;
method Copy: InstanceType; virtual;
end;
Manager = public class(Employee)
public
property Underlings: List<Person>;
method Copy: InstanceType; override;
end;
public class Employee
{
public string Name { get; set; }
public virtual InstanceType Copy() { ... }
}
public class Manager : Employee
{
public List<Person> Underlings { get; set; }
public override InstanceType Copy() { ... }
}
public class Employee {
public var Name
public func Copy() -> Self { ... }
}
public class Manager : Employee
{
public var Underlings: [Person]
public override func Copy() -> Self { ... }
}
public class Employee
{
public string Name { __get; __set; }
public virtual InstanceType Copy() { ... }
}
public class Manager : Employee
{
public List<Person> Underlings { __get; __set; }
public override InstanceType Copy() { ... }
}
Because the compiler knows that lMyManager
is a Manager
, it know can infer that the result of Copy()
will also be a Manager
instance. So the following code now works, without cast:
var m := lMyManager.Copy();
var u := m.Underlings.Count;
var m = lMyManager.Copy();
var u = m.Underlings.Count;
let m = lMyManager.Copy()
let u = m.Underlings.Count
var m = lMyManager.Copy();
var u = m.Underlings.Count;
But of course Copy()
still behaves fully polymorphically, like any virtual/override method. When calling Copy()
on an instance of a Manager
that's stored in a variable typed Employee
, the same override Copy()
method will be called at runtime – but of course in that case, the compiler will merely assume the result is an Employee
:
var e := lSomeEmployee.Copy(); // could be a Manager, but we don't know
var u := e.Underlings.Count; // fails! we can't assume e is a Manager, here
var e = lSomeEmployee.Copy(); // could be a Manager, but we don't know
var u = e.Underlings.Count; // fails! we can't assume e is a Manager, here
var e = lSomeEmployee.Copy() // could be a Manager, but we don't know
var u = e.Underlings.Count // fails! we can't assume e is a Manager, here
var e := lSomeEmployee.Copy(); // could be a Manager, but we don't know
var u := e.Underlings.Count; // fails! we can't assume e is a Manager, here
InstanceType in Operators
InstanceType
can also be used when defining Custom Operators. Operators must use the declaring type for at least one parameter and/or result type, and rather than specifying the actual type name, Instancetype
can be used as a a placeholder. This makes operators more robust to type renames, and makes it easier to copy/paste and reuse code when declaring similar operators on different types.
type
MyClass = public class
operator Implicit(aValue: String): InstanceType;
begin
result := ... //convert from String to MyClass
end;
end;
public class MyClass
{
public static implicit operator InstanceType(string value)
{
return ... //convert from String to MyClass
}
}
public class MyClass {
func __implicit(_ aValue: String) -> InstanceType {
return ... //convert from String to MyClass
}
}
See Also
Version Notes
As of Elements 10, InstanceType
is now supported on all platforms, not just Cocoa (where it has always been available).