Статическое приведение типов

Программирование на Atlantis: приемы и подходы, обмен опытом

Модераторы: larin, Screw

Kefiro
Сообщения: 22
Зарегистрирован: Чт, 08/09/2011 17:13
Имя Фамилия: Андрей Пеньков

Статическое приведение типов

Сообщение Kefiro » Вт, 03/04/2012 17:38

Помогите, пожалуйста, придумать, как в випе организовать явное приведение типов.
Вот такой пример.

Код: Выделить всё

vipInterface Parent;
   function equals(p : Parent);
end;

vipInterface Child(Parent);
  property x : integer;
  function equals(p : Parent);
end;

interface Child;
   var
      xf : integer;

  property x : integer read xf write xf := result;

  function equals(p : Parent);
  {
    var c : Child;
    c := Child(p);
    if (c = nullRef)
       result := false;
    else
       result := (c.x = xf);
  }
end.


Два экземляра Child равны, если у них равны свойства x. В теле функции equals хочу определить привести p к переменной типа Child. Как это сделать? Компилятор не пропускает приведение c := Child(p). Я так понимаю, что он не умеет сужать тип (правда, я не понимаю тогда, для чего вообще служит статическое приведение, мне всегда казалось, что как раз для сужения - в обратную сторону вообще явного приведения не нужно).
Особенно остро встаёт вопрос, когда у Parent большое множество разных дочерних типов, в которых свои собственные реализации родительского метода.
Спасибо.

Аватара пользователя
Screw
корпорация
Сообщения: 73
Зарегистрирован: Пт, 14/09/2007 22:54
Имя Фамилия: Виталий Корзюк
Откуда: ТопСофт
Контактная информация:

Re: Статическое приведение типов

Сообщение Screw » Пт, 06/04/2012 10:10

Child(p) - это не сужение, а, скорее, возведение, расширение к наследнику. Операция недопустимая. К базовому - можно. К наследнику - нельзя. В vip нет виртуальных функций в чистом виде, поэтому и попытка реализовать метод Equals на vip-интерфейсах не имеет смысла. Зато эта задача в принципе легко решается с помощью obj-интерфейсов.

Код: Выделить всё

objinterface IComparable
  property Criteria1: SomeType1;
  ...
  property CriteriaN: SomeTypeN;
end;

Тогда утверждение равенства звучит так: "две реализации IComparable равны, если все или некоторая комбинация их критериев равны". На месте CriteriaXXXX может стоять любая значимая характеристика из предметной области. То есть, в принципе можно сравнивать две совершенно различные реализации. Например, если критерием равенства является степень гладкости, плоскости, ровности объекта (Plainness), то Стол и Дорога вполне могут быть "равны".

Если нужно организовать сравнение объектов, "произрастающих" от одного корня, то функцию сравнения можно разместить в базовом объекте:

Код: Выделить всё

vipinterface Base(IComparable);
  function Equals(Another: IComparable): boolean;
end;

interface Base;
  ...
  property Criteria1: SomeType1 read ...;
  ...
  function Equals(Another: IComparable): boolean;
  {
    var Me: IComparable;
    Me := IComparable(Self);
    Result :=
      (Me.Criteria1 = Another.Criteria1)
      and (Me.Criteria2 = Another.Criteria2)
      ...;
  }
  ...
end.

В наследниках можно реализовать свои собственные правила извлечения значений критериев, а можно оставить и родительские.

Сравнение можно реализовать "снаружи". Этот вариант мне лично нравится больше, т.к. позволяет описывать много различных способов сравнения ("больше", "меньше", "подобен", "конгруэнтен"):

Код: Выделить всё

function Equals(One, Another: IComparable): boolean;


Обе схемы позволяют описать асимметричное сравнение, при котором A = B, но B <> A. Например, с некоторой точки зрения Стул можно использовать как Стол, но Стол нельзя использовать как Стул :-)

Всегда лучше абстрагироваться от частностей и работать с obj-интерфейсами, описывающими независимые сущности или независимые наборы характеристик их возможных реализаций. Obj-интерфейсы нужны для получения полиморфизма. Наследование vip-интерфейсов используйте только для того, чтобы копировать и расширять реализацию.

Kefiro
Сообщения: 22
Зарегистрирован: Чт, 08/09/2011 17:13
Имя Фамилия: Андрей Пеньков

Re: Статическое приведение типов

Сообщение Kefiro » Пт, 06/04/2012 15:42

Виталя, спасибо за ответ.
Я понял, как мне кажется, твою идею, и по большому счёту приблизительно так и поступал в подобных случаях - введением объектного интерфейса. Но в данной задаче мне, допустим, надо учитывать ещё один критерий для сравнения - два объекта могут быть равны в том случае и только в том случае, когда они одного типа. Добавить этот критерий в IComparable в качестве свойства я не могу, так же как и в родительской интерфейсе не могу описать сравнение для всех возможных типов, чтобы не ограничивать наследование.
Я всё-таки не оставил пока идею приведения и пробую такой способ: использование "нетипизированной" ссылки objRef, которую на этапе компиляции можно привести к любому типу. Есть два варианта: либо делать параметр типа objRef, а не Parent, либо вводить дополнительную локальную переменную:

Код: Выделить всё

var v : objRef;
v := p;
c := Child(v);

Насколько опасно использовать такой способ? Конечно, если в качестве параметра передать, например, не Child, а OtherChild (который тоже потомок Parent), то приведение в ходе выполнения не пройдёт, переменная с будет иметь значение nullRef, и вроде всё в порядке. То же самое произойдёт, если передавать объект типа Parent.
Где можно нарваться на ошибку при таком подходе?


Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 4 гостя