Dmitry Onoshko 0 Posted November 3 Consider a container: type TCustomItem = class protected FContainer: // ??? end; TCustomContainer<T: TCustomItem> = class abstract protected FItems: TArray<T>; ... procedure HandleItemNotification(AItem: T); end; The items are supposed to be classes derived from common ancestor in parallel with containers: type TFooItem = class(TCustomItem); TFooContainer = class(TCustomContainer<TFooItem>); TBarItem = class(TCustomItem); TBarContainer = class(TCustomContainer<TBarItem>); An item should store a pointer to its container for notification purposes. But TFooItem should never be used with TBarContainer or vice versa. I seem to get closer when I declare the TCustomItem as inner class: type TCustomContainer<T: TCustomItem> = class abstract public type TCustomItem = class protected FContainer: TCustomContainer<T>; end; protected FItems: TArray<T>; ... procedure HandleItemNotification(AItem: T); end; But then, when I call procedure TCustomContainer<T>.TCustomItem.SomeMethod; begin ... FContainer.HandleItemNotification(Self); ,,, end; inside TCustomItem method, I get “Incompatible types: T and UnitName.TCustomContainer<T>.TCustomItem”. Is this even possible? Share this post Link to post
havrlisan 24 Posted November 3 12 minutes ago, Dmitry Onoshko said: type TCustomContainer<T: TCustomItem> = class abstract public type TCustomItem = class protected FContainer: TCustomContainer<T>; end; protected FItems: TArray<T>; ... procedure HandleItemNotification(AItem: T); end; But then, when I call procedure TCustomContainer<T>.TCustomItem.SomeMethod; begin ... FContainer.HandleItemNotification(Self); ,,, end; inside TCustomItem method, I get “Incompatible types: T and UnitName.TCustomContainer<T>.TCustomItem”. It is not possible to define TCustomContainer<T: TCustomItem> if TCustomItem is its internal public type. This only compiles (up to your error) because there is another TCustomItem defined outside that class. Doing this with classes only is impossible. You cannot have both a T that is a TCustomItem within TCustomContainer<T> and a FContainer that is a TCustomContainer<T> within TCustomItem. The relation cannot be bi-directional in this scenario. A closer implementation to what you're trying to achieve is possible with interfaces, but that comes with its own set of issues. This post has an example of what you're trying to achieve with interfaces: Also, IMO it is not a good idea to couple your classes like you're trying to do here. A TCustomItem should not directly call the TCustomContainer's HandleItemNotification; you should handle that either via an observer pattern or with events. 1 Share this post Link to post
Stefan Glienke 2002 Posted November 7 (edited) type TCustomItem = class; TCustomContainer = class abstract protected procedure HandleItemNotification(AItem: TCustomItem); virtual; abstract; end; TCustomItem = class protected FContainer: TCustomContainer; end; TCustomContainer<T: TCustomItem> = class abstract(TCustomContainer) protected FItems: TArray<T>; procedure HandleItemNotification(AItem: TCustomItem); overload; override; final; procedure HandleItemNotification(AItem: T); reintroduce; overload; end; TFooItem = class(TCustomItem) procedure SomeMethod; end; TFooContainer = class(TCustomContainer<TFooItem>); procedure TCustomContainer<T>.HandleItemNotification(AItem: T); begin // ... end; procedure TCustomContainer<T>.HandleItemNotification(AItem: TCustomItem); begin HandleItemNotification(T(AItem)); end; procedure TFooItem.SomeMethod; begin FContainer.HandleItemNotification(Self); end; var foo: TFooItem; begin foo := TFooItem.Create; foo.FContainer := TFooContainer.Create; foo.SomeMethod; end. I suggest moving as much code into the non generic base classes as possible. Given the constraint on TCustomItem this should even be reasonably easy to achieve. This will prevent causing larger than necessary binary sizes and longer than necessary compiletimes. Edited November 7 by Stefan Glienke Share this post Link to post