Задать вопрос
@Lasersquad

Почему не компилируется дженерик интерфейс в дженерик классе (Delphi)?

Не могу решить проблему с ошибкой компиляции в Delphi.

В коде у меня дженерик класс TMySpecialArray который умеет хранить и сравнивать значения любого типа. Для того что бы он мог сразвнивать значения любого типа класс имеет дженерик параметр CmpT: IComparer.
Когда я использую мой класс просто в коде то все работает нормально.
Если я использую этот мой класс из другого дженерик класса (см. TMySpecialArrayTest в коде ниже) я получаю ошибку коммпиляции.
"[dcc64 Error] Unit1.pas(45): E2514 Type parameter 'CmpT' must support interface 'System.Generics.Defaults.IComparer'"

Помогите понять почему TMySpecialArray не работает если использован и другого дженерик класса.
Использую Delphi 12.3.
type
  TMySpecialArray<T:constructor; CmpT: IComparer<T>> = class
  private
    FComparer: CmpT;
  end;

  TMyComparer<TT> = class(TInterfacedObject, IComparer<TT>)
  public
    function Compare(const Left, Right: TT): Integer;
  end;


  TMySpecialArrayTest<V: constructor> = class
  public
    Arr: TMySpecialArray<V, TMyComparer<V> >; //!!! здесь ошибка компиляции
  end;

var
  var1: TMySpecialArray<Integer, TMyComparer<Integer>>;
  var2: TMySpecialArray<TObject, TMyComparer<TObject>>;
  var3: TMySpecialArray<string, TMyComparer<string>>;
  var4: TMySpecialArrayTest<Integer>;
begin
   var4 := TMySpecialArrayTest<Integer>.Create;
end;
  • Вопрос задан
  • 137 просмотров
Подписаться 2 Средний Комментировать
Пригласить эксперта
Ответы на вопрос 2
OCTAGRAM
@OCTAGRAM
Иногда помогает разбить generic на несколько ступенек при помощи пустых record. Внутри record могут быть классы, интерфейсы, переименования, кроме generic переименований. То есть,

type
  TGenericB<T> = TGenericA<T>; // так нельзя

  TSomeContainer<T> = record
  public
    type
      TGenericB = TGenericA<T>; // так можно
  end;


Таким способом можно попробовать T захватить в первой ступеньке generic, а CmpT сделать второй ступенькой:

type
  TMySpecial<T: constructor>  = record
  public
    type
      IComparer = IComparer<T>;
      TMyArray<CmpT: IComparer> = class
        // …
      end;
  end;


P.S. В System.Generics.Defaults есть абстрактный класс TComparer, и можно требовать наследование от него вместо поддержки интерфейса. Или внутри record свой абстрактный класс объявить, например, с виртуальным конструктором, и его использовать вместо ограничения generic "constructor".
Ответ написан
Комментировать
@Lasersquad Автор вопроса
Последовал вашему совету. Спасибо!

Я вместо IComparer теперь требую наследника от TComparer.
Т.е. убрал все дженерик интерфейсы из кода. Заменил классами.
После такой замены все стало компилироваться на ура.
Не совсем понятно почему интерфейсы здесь не компилируются но с классами заработало.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы