Страницы

пятница, 24 июля 2009 г.

Обобщенное программирование(generics) в Delphi 2009 для Win32. Часть 4. Создание обобщенной записи.

В пособии также рассматриваются анонимные методы и процедурные ссылки.



Словарь терминов
  • generics - дженерики, генерики, параметризованные классы, шаблоны, обобщения;
  • Типы данных: string – строка, record – запись;
  • implicit conversion - неявное приведение (типа);

IV. Создание обобщённой записи

Давайте изучим кое-что ещё и попробуем разработать простенькую, но полезную запись TNullable<T>. Идея состоит в том, чтобы создать запись, которая сможет иметь значения типа T, либо nil. Вы наверное уже задумывались о необходимости такого типа, например для представления значений Null из баз данных.

Эта запись будет содержать два поля: FValue типа T, и булевое поле FIsNil. Для чтения полей, созданы два свойства. Мы будем использовать лишь операторы неявного приведения (типов), чтобы построить значения этого типа.

unit Generics.Nullable;
interface
type
  TNullable<T> = record
  private
    FValue: T;
    FIsNil: Boolean;
  public
    class operator Implicit(const Value: T): TNullable<T>;
    class operator Implicit(Value: Pointer): TNullable<T>;
    class operator Implicit(const Value: TNullable<T>): T;
    property IsNil: Boolean read FIsNil;
    property Value: T read FValue;
  end;
image

За дополнительной информацией о перегрузке операторов я советую обратиться к франкоязычной статье La surcharge d'opérateurs sous Delphi 2006 Win32, которую написал Laurent Dardenne (примечание переводчика: я не нашёл аналогичного материала на русском языке).

Таким образом, это неизменяемый тип (объект, чьё состояние не может быть изменено после создания).

Реализация трех операторов преобразования достаточно проста. Второй (с параметром Pointer) нужен для того, чтобы можно было делать присвоение : = nil.

uses
  SysUtils;
   
resourcestring
  sCantConvertNil = 'Cannot convert to nil';
  sOnlyValidValueIsNil = 'The only valid value is nil';
   
class operator TNullable<T>.Implicit(const Value: T): TNullable<T>;
begin
  Result.FValue := Value;
  Result.FIsNil := False;
end;
   
class operator TNullable<T>.Implicit(Value: Pointer): TNullable<T>;
begin
  Assert(Value = nil, sOnlyValidValueIsNil);
  Result.FIsNil := True;
end;
   
class operator TNullable<T>.Implicit(const Value: TNullable<T>): T;
begin
  if Value.IsNil then
    raise EConvertError.Create(sCantConvertNil);

  Result := Value.FValue;
end;

Использовать эту запись можно так:

var
 Value: Integer;
 NullValue: TNullable<Integer>;
begin
 NullValue := 5;
 WriteLn(Integer(NullValue));
 NullValue := nil;
 if NullValue.IsNil then
   WriteLn('nil')
 else
   WriteLn(NullValue.Value);
   
 NullValue := 10;
 Value := NullValue;
 WriteLn(Value);
end;

Результат выполнения будет таким:

5

nil

10

Всё понятно? Это просто чудесно, учитывая, что я не дал ни единого пояснения. Это лишний раз доказывает, что дженерики это также просто как пирог :-). (примечание переводчика: не знаю почему автор считает что пирог – это просто. Я как-то пробовал испечь один, и скажу честно - программирование намного проще) (в оригинале была идиома "as easy as pie", которая переводится как "проще простого")

Полный исходный код Generics.Nullable доступен для скачивания в конце учебника.



Публикации по теме

4 комментария:

  1. easy as pie - идиоматическое выражение, обозначающее "очень простое действие" (этимология - отличие cake от pie, то есть сравнительная простота). Адекватный перевод - "как два пальца ... " (нет, грубо), лучше "как два байта переслать". Но есть много других хороших русских выражений.

    ОтветитьУдалить
  2. Спасибо за замечание Alex (особенно за этимологическую справку)!

    ОтветитьУдалить
  3. п.с. я всё-таки оставлю свою дословную версию (к ней довольно неплохо приклеилась моя несмешная шутка), да и для использования любой из этиъ фраз придётся заменять простоту дженериков на простоту действий над ними, а это как-то не комильфо.

    ОтветитьУдалить
  4. Тогда лучше перевести "easy as pie", как "проще пареной репы". Хотя, на вкус...

    ОтветитьУдалить