A little example
TTest<T> = class
private
f : T;
public
function ToString : string;
end;
If is an object then this should work
TTest<T>.ToString;
begin
Result := f.ToString;
end;
But what happens when is say an integer? This would be ok in .net. of course.
I know it won't work, but how do I code this to work with objects AND simple types?
There are three reasons why Delphi doesn't let you do what you are trying to do in your second example - call the ToString method on a value of an unconstrained type parameter type (or at least that's what I think you were trying to show, since TObject.ToString is an instance method, not a class method, so T.ToString would not work even for TObject).
Delphi does not have a rooted type system, and there are very few operations that are common to all types. These operations - copying, assignment, creating locations (fields, locals, parameters, arrays) - are the only operations that are guaranteed to be available on all possible values of the type parameter.
Following on from 1, why are the operations restricted to these? Why not permit the operations in the generic class, and only give errors at instantiation time? Well, the first part of the reason is that the design was originally intended to maximize compatibility with .NET and dccil, so things that .NET generics did not permit did not have significant affordances in the Win32 generics design.
The second justification for the design is that check-only-at-instantiation-time is problematic. The most famous parametric polymorphism implementation that uses this approach is C++ templates, and it is also famous for its cryptic error messages, as you e.g. try to pass the wrong kind of iterator to an algorithm, and get odd complaints about overloaded operators not being found. The deeper the polymorphism, the worse the problem. In fact, it's so bad that C++ is itself rectifying this fault in the form of C++ 0x Concepts.
Hopefully you now understand why operations that aren't guaranteed to be available via constraints cannot be used. However, you can get out of this restriction relatively easily, by providing the operation in the form of a method reference or interface implementation, as Gamecat suggested.
In the future, generics in Delphi for Win32 will likely be extended along similar lines to C++ 0x Concepts, or Haskell type classes, such that type parameters may be constrained to have certain methods, functions and operators available. If it went along type class lines, then type inference may proceed in that fashion too.
The last example will not work. You need to add a constraint in order to use methods. In this case TObject will be enough:
TTest<T: TObject>.ToString;
begin
Result := T.ToString;
end;
You can use simple types with unrestrained generics, but you are very limited in the use. Because the only valid operations are assignment and comparison (equal and not equal).
In Delphi simple types are no classes, so they don't have methods. But you can do the following:
type
TToString<T> = reference to function(const AValue: T): string;
TGenContainer<T> = class
private
FValue: T;
FToString : TToString<T>;
public
constructor Create(const AToString: TToString<T>);
function ToString: string;
property Value: T read FValue write FValue;
end;
constructor TGenContainer<T>.Create(const AToString: TToString<T>);
begin
FToString := AToString;
end;
function TGenContainer<T>.ToString: string;
begin
Result := FToString(FValue);
end;
procedure TForm2.Button1Click(Sender: TObject);
var
gen : TGenContainer<Integer>;
begin
gen := TGenContainer<Integer>.Create(
function(const AValue: Integer): string
begin
Result := IntToStr(AValue);
end);
try
gen.Value := 17;
Memo1.Lines.Add(gen.ToString);
finally
gen.Free;
end;
end;
This works fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With