Обобщения не разрешают типы методов правильно
Учтите следующее:
{$APPTYPE CONSOLE}
uses
Generics.Collections;
type
TObjProc = procedure of object;
TFoo = class
public procedure DoFoo;
public procedure DoBar;
end;
procedure TFoo.DoFoo;
begin
WriteLn('foo');
end;
procedure TFoo.DoBar;
begin
WriteLn('bar');
end;
var
ProcList : TList<TObjProc>;
Foo : TFoo;
aProc : TObjProc;
begin
Foo := TFoo.Create;
ProcList := TList<TObjProc>.Create;
ProcList.Add(Foo.DoFoo);
ProcList.Add(Foo.DoBar);
for aProc in ProcList do aProc;
ReadLn;
end.
Это дает ожидаемый результат
foo
bar
Теперь предположим, что мы хотим назначить процедуру из списка. Перечисление работ, как указано выше. Это также работает:
aProc := ProcList.Items[0];
aProc;
Но это выдает ошибку компилятора:
aProc := ProcList.First;
// E2010 Incompatible types:
//'procedure, untyped pointer or untyped parameter' and 'TObjProc'
Что вдвойне странно, так как
function TList<T>.First: T;
begin
Result := Items[0];
end;
Так, что происходит?
Влияет ли это и на новые версии Delphi? Я испытываю искушение проверить это, если есть разумное ожидание, что это должно работать (что, я думаю, так и есть).
1 ответ
Это не ошибка компилятора, и в действительности эта проблема не связана с использованием вами дженериков. И то и другое First
а также Last
являются функциями, поэтому компилятор не может сказать, хотите ли вы вызвать их или ссылаться на них. Будьте явными и дайте компилятору понять, что вы хотите вызвать функцию, поставив символы скобок.
aProc := ProcList.First();
aProc := ProcList.Last();
Тем не менее, вы снова были застигнуты врасплох решением разрешить опускать парены при вызове процедур и функций. Это дизайнерское решение, хотя и выглядело так привлекательно, когда оно было принято, теперь выглядит не так, как сейчас, когда процедурные типы так широко используются в современных стилях кодирования.
Когда ты пишешь ProcList.First
Компилятор сталкивается с неоднозначностью. Вы хотите вызвать функцию или хотите назвать функцию процедурным типом? Во многих случаях компилятор не может разрешить неоднозначность, но это не тот случай, когда выражение находится справа от оператора присваивания. Столкнувшись с этой неоднозначностью, компилятор предполагает, что вы имеете в виду обращение к функции.
Он принимает этот выбор, потому что другой выбор будет хуже. По крайней мере, таким образом вы можете указать парены и явно указать, что вы имеете в виду для вызова функции. Если бы компилятор пошел по другому пути, то вам пришлось бы искать способ сказать ему, что вы хотели обратиться к функции.
В конце концов, если First
а также Last
был реализован как свойства, не было бы никакой двусмысленности.