TGenericClass <T>, содержащий TObjectList <T>, не компилируется - PullRequest
3 голосов
/ 30 июня 2009

Я пытаюсь написать универсальный класс, содержащий обобщенный 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 ]

7 голосов
/ 30 июня 2009

Это известная ошибка с компилятором D2009. Скорее всего, это будет исправлено в ближайшее время, либо в обновлении, либо в исправлении для 2009 года, либо в Delphi 2010 (Weaver) после его выпуска. До тех пор, к сожалению, вам нужен какой-то обходной путь. (

7 голосов
/ 30 июня 2009

Универсальные типы могут иметь несколько ограничений:

  • Имя класса, универсальный тип должен относиться к этому классу потомка класса.
  • Имя интерфейса, универсальный тип должен реализовывать этот интерфейс.
  • 'class', универсальный тип должен быть классом (его нельзя комбинировать с именем класса).
  • 'запись', универсальный тип должен быть записью.
  • 'Конструктор', немного расплывчато, но вы можете создавать экземпляры универсального типа класса.

Если вы создаете универсальный шаблон, который использует другие универсальные шаблоны, вам необходимо скопировать ограничения, иначе он не будет работать. В вашем случае TObjectList имеет ограничение класса. Это означает, что вашему T тоже нужно это ограничение.

К сожалению, это не может быть объединено с ограничением именованного класса.

Поэтому я советую вам использовать интерфейс, их можно комбинировать:

type
  IItem = interface end;
  TItem = class (TInterfacedObject, IItem) end;
  TGenericClass<T: class, IItem> = class
  private
    FSimpleList: TList<T>;
    FObjectList: TObjectList<T>;
  end;

Кроме того, вы должны сделать свои поля приватными, иначе каждый может их изменить.

3 голосов
/ 30 июня 2009

Мне нравится ответ 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;

Добавить является примером того, как получить доступ к списку объектов. Оказывается, вы можете передать Item в ObjectList.Add без каких-либо проблем:

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 вполне может подойти.

...