TGenericClass, содержащий TObjectList, не компилируется
Я пытаюсь написать универсальный класс, который содержит универсальный TObjectList, который должен содержать только элементы TItem.
uses
Generics.Collections;
type
TItem = class
end;
TGenericClass<T: TItem> = class
public
SimpleList: TList<T>; // This compiles
ObjectList: TObjectList<T>; // This doesn't compile: Compiler complaints that "T is not a class type"
end;
Это неправильный синтаксис? Кстати: tgenericclass компилируется, но тогда элементы в списке больше не являются TItem, чего я не хочу.
3 ответа:
Это известная ошибка с компилятором D2009. Скорее всего, он будет исправлен в ближайшее время, либо в обновлении или исправлении для 2009 года, либо в Delphi 2010 (Weaver), как только он будет выпущен. А до тех пор, к сожалению, вам нужен какой-то обходной путь. : (
Универсальные типы могут иметь несколько ограничений:
- имя класса, общий тип должен принадлежать этому классу потомка класса.
- имя интерфейса, универсальный тип должен реализовать этот интерфейс.
- 'class', универсальный тип должен быть классом (это не может быть объединено с именем класса).
- 'record', универсальный тип должен быть записью.
- 'конструктор', немного расплывчатый, но вы можете создавать экземпляры универсального типа класса.
Если вы создайте дженерик, который использует другие дженерики, вам нужно скопировать ограничения, иначе он не будет работать. В вашем случае tobjectlist имеет ограничение класса. Это означает, что ваше Т тоже нуждается в этом ограничении.
К сожалению, это не может быть объединено с ограничением именованного класса.
Поэтому я советую вам использовать интерфейс, они могут быть объединены:
type IItem = interface end; TItem = class (TInterfacedObject, IItem) end; TGenericClass<T: class, IItem> = class private FSimpleList: TList<T>; FObjectList: TObjectList<T>; end;
Кроме того, вы должны сделать свои поля закрытыми, иначе их может изменить кто угодно.
Мне нравится ответ GameCat (дал его +1) для описания ограничений класса.
У меня есть небольшая модификация вашего кода, которая работает. Обратите внимание, что поскольку вы дали ограничение, чтобы сказать, что T должен быть потомком TItem, вы можете просто объявить ObjectList как
TObjectList<TItem>
- нет необходимости использовать T здесь.В качестве альтернативы можно создать своего рода прокси. Во-первых, обратите внимание на комментарий GameCat о том, что поля являются частными.
type TGenericClass<T: TItem> = class private type D = class(TItem); // Proxy to get your T into and object list private SimpleList: TList<T>; ObjectList: TObjectList<D>; // Compiles now, but there is that type issue public procedure Add(Item: T); // No direct access to ObjectList end;
Add является примером того, как получить доступ к список объектов. Как оказалось, Вы можете передать элемент в ObjectList.Добавьте без малейших затруднений:
procedure TGenericClass<T>.Add(Item: T); begin ObjectList.Add(Item); end;
Я думаю, что это может быть ошибка, поэтому, чтобы защитить себя от того, что исправлено:
procedure TGenericClass<T>.Add(Item: T); var Obj: TObject; begin Obj := Item; ObjectList.Add(D(Obj)); end;
Однако, учитывая ваш сценарий, я бы сказал, что TObjectList должен делать все отлично.