Как мне узнать, является ли тип generi c нулевым, без использования ограничений в Delphi? - PullRequest
4 голосов
/ 25 января 2020

Я хочу проверить, является ли обобщенный тип c "допустимым" без использования ограничений.

Другими словами, я хочу написать следующий код:

class function TMaybe<T>.FromValue(aValue: T): TMaybe<T>;
begin
  if T <> nil then
  begin
    Result := TMaybe<T>.Some(aValue);
  end else
  begin
    Result := TMaybe<T>.None;
  end;
end;

Однако это не компилируется с ошибкой:

E2571 Type parameter 'T' doesn't have class or interface constraint

Очевидно, для Класс, подобный этому, я хотел бы иметь возможность иметь любой тип, будь то TMaybe.

Есть ли способ проверить, является ли тип без контрацепции "допустимым", то есть не нулевым? (Меня не волнуют пустые строки и т. Д. c.)

Должен ли я написать TypeIsEmpty<T>(aValue: T): Boolean, который использует TypInfo, чтобы выяснить это? Я бы хотел этого избежать.

1 Ответ

4 голосов
/ 25 января 2020

Попробуйте что-то вроде этого:

type
  PMethod = ^TMethod;

class function TMaybe<T>.FromValue(aValue: T): TMaybe<T>;
begin
  {
  the *undocumented* IsManagedType() intrinsic function returns True if T
  is an interface, string, or dynamic array, but it also returns True if T
  is a record containing such a field. Since a record can't be compared to
  nil, IsManagedType(T) is not useful here.

  Using the *undocumented* GetTypeKind() intrinsic function can be used
  instead to handle ONLY nil-able types...
  }
  // if IsManagedType(T) then...
  case GetTypeKind(T) of
    tkString, tkClass, tkLString, tkWString, tkInterface, tkDynArray, tkUString:
    begin
      if PPointer(@aValue)^ = nil then
        Exit(TMaybe<T>.None);
    end;
    tkMethod:
    begin
      if (PMethod(@aValue)^.Data = nil) or (PMethod(@aValue)^.Code = nil) then
        Exit(TMaybe<T>.None);
    end;
  end;
  Exit(TMaybe<T>.Some(aValue));
end;

Intrinsi c Функции, такие как GetTypeKind()IsManagedType()), оцениваются во время компиляции и как такие ветви кода с оценкой False оптимизируются из окончательного исполняемого файла. Тем не менее, как @DavidHeffernan упоминает в комментариях, компилятор проверяет синтаксис кода, прежде чем он создает экземпляр generi c, следовательно, PPointer typecast, чтобы обойти это.

Итак, если вы установите T в с нулевым типом, например String, компилятор сможет оптимизировать код до следующего:

class function TMaybe<String>.FromValue(aValue: String): TMaybe<String>;
begin
  if Pointer(aValue) = nil then
    Exit(TMaybe<String>.None);
  Exit(TMaybe<String>.Some(aValue));
end;

И если вы установите T для типа, не являющегося ноль, например, Integer компилятор сможет оптимизировать код до этого:

class function TMaybe<Integer>.FromValue(aValue: Integer): TMaybe<Integer>;
begin
  Exit(TMaybe<Integer>.Some(aValue));
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...