Использование собственного класса в качестве ограничения параметра типа в объявлении класса

У меня есть следующее объявление класса в Delphi XE8:

TestClass = class;
TestClass = class
  function test<T: TestClass>(supplier: TFunc<T>): T; // Compiler error
end;

Что вызывает следующую ошибку компилятора:

E2086 Type 'TestClass' is not yet completely defined

Когда я добавляю в смесь еще один класс и использую его в качестве ограничения, он работает нормально:

AnotherTestClass = class
end;

TestClass = class;
TestClass = class
  function test<T: AnotherTestClass>(supplier: TFunc<T>): T; // No Error
end;

Я подозреваю, что проблема в том, что прямое объявление типа еще недостаточно сообщает Delphi о типе TestClass. Возможно, это более очевидно, так как следующая попытка обойти проблему вызывает ту же самую ошибку компилятора в другой строке:

TestClass = class;
AnotherTestClass = class (TestClass) // Compiler Error
end;
TestClass = class
  function test<T: AnotherTestClass>(supplier: TFunc<T>): T;
end;

Я делаю что-то не так, и если нет, то есть ли способ обойти эту проблему?


person overactor    schedule 05.01.2016    source источник
comment
Похоже, это ошибка компилятора, поэтому я отправил отчет об ошибке. .   -  person overactor    schedule 05.01.2016
comment
Конечно код TestClass = class; AnotherTestClass = class (TestClass) // Ошибка компилятора end; должна привести к показанной ошибке, поскольку Delphi является однопроходным компилятором. Но я не вижу причин, по которым то же самое должно относиться к конструкции, которую вы пытаетесь создать. На данный момент имеется достаточно информации, чтобы определение было действительным.   -  person Dsm    schedule 06.01.2016
comment
@Dsm согласился, но, поскольку ошибка компилятора такая же, я решил, что это намек на то, что идет не так. Однако это создает проблему: что, если мне нужны два класса, которые используют друг друга в качестве ограничения параметра типа?   -  person overactor    schedule 06.01.2016
comment
Я думаю, что та же проблема относится к вашим двум классам, использующим друг друга в качестве ограничений параметра типа. Очевидно, что два класса должны находиться в одном блоке и в одном и том же блоке типа, как если бы классы ссылались друг на друга, но я полагаю, что вы столкнетесь с той же проблемой. И так же, как и в вашем примере, я не вижу причин, по которым компилятор не должен разрешать это.   -  person Dsm    schedule 07.01.2016


Ответы (1)


Вы не делаете ничего плохого. То, что вы пытаетесь сделать, должно быть возможно, но компилятор, на мой взгляд, неисправен. Нет реального способа обойти это без полного изменения дизайна. Один из способов обойти эту проблему — применить ограничение во время выполнения. Однако, на мой взгляд, это будет считаться полной сменой дизайна.

Обратите внимание, что в .net то, что вы пытаетесь сделать, вполне возможно:

class MyClass
{
    private static T test<T>(Func<T> arg) where T : MyClass
    {
        return null;
    }
}

Функция дженериков Delphi была основана на дженериках .net, и я подозреваю, что проблема, с которой вы сталкиваетесь, связана с недосмотром со стороны разработчиков Delphi.

Вы должны отправить отчет об ошибке / запрос функции.

Обновление 1

LU RD предлагает лучший обходной путь. Используйте помощник класса:

type
  TestClass = class
  end;

  TestClassHelper = class helper for TestClass
    function test<T: TestClass>(supplier: TFunc<T>): T;
  end;

Это позволит вам протестировать ограничение во время компиляции. Тем не менее, это заставляет вас определять метод вне функции, что является неопрятным, и мешает вам использовать помощник класса для любых других целей. Таким образом, вы все равно должны отправить отчет об ошибке / запрос функции, на мой взгляд.

Обновление 2

Отчет об ошибке: RSP-13348

person David Heffernan    schedule 05.01.2016
comment
Перемещение функции в помощник класса компилирует: ` TestClassHelper = помощник класса для функции TestClass test‹T: TestClass›(поставщик: TFunc‹T›): T; конец; `. - person LU RD; 05.01.2016
comment
@LURD Хороший улов, на самом деле это жизнеспособный обходной путь. Поведение и API класса должны быть идентичными задуманному, верно? Это по-прежнему серьезное упущение в компиляторе, поэтому я все равно отправлю отчет об ошибке. - person overactor; 05.01.2016
comment
Пожалуйста, разместите здесь ссылку на отчет об ошибке. Люди могут проголосовать за него, если они хотят, чтобы дженерики были более ортогональными и полными. - person Warren P; 05.01.2016
comment
@WarrenP Я только что был на этом :) Вот отчет об ошибке. Имейте в виду, я никогда раньше не писал запрос функции. - person overactor; 05.01.2016
comment
На мой взгляд, лучшим обходным путем является использование абстрактного базового класса для вашего ограничения. Это упростит издевательство над TestClass. Добавьте любые виртуальные абстрактные) функции, составляющие сигнатуру типа. - person Warren P; 05.01.2016