#delphi #generics #interface
#delphi #дженерики #интерфейс
Вопрос:
Я использую Delphi XE2. В настоящее время у меня есть объектно-ориентированная модель, и каждый объект модели может иметь несколько валидаторов. Вот упрощенная реализация универсального абстрактного класса validator. Конкретные классы валидатора могут переопределять DoValidate, и им не нужно приводить объект модели. Средство проверки используется с использованием его интерфейса IValidator.
unit ObjectBasedValidator;
interface
uses
System.SysUtils,
System.Generics.Collections;
type
TModelEntity = class
end;
type
IValidator = interface
procedure Validate(aEntity: TModelEntity; aResult: string);
end;
TValidator<T: TModelEntity> = class(TInterfacedObject, IValidator)
private
protected
procedure DoValidate(aEntity: T; aResult: string); virtual; abstract;
public
procedure Validate(aEntity: TModelEntity; aResult: string);
end;
implementation
{ TValidator<T> }
procedure TValidator<T>.Validate(aEntity: TModelEntity; aResult: string);
begin
if not (aEntity is T) then
Exit;
DoValidate(aEntity as T, aResult);
end;
end.
Теперь я пытаюсь изменить объектную модель на интерфейсную. Итак, вот обновленный модуль проверки:
unit InterfaceBasedValidator;
interface
type
IModelEntity = interface
end;
type
IValidator = interface
procedure Validate(aEntity: IModelEntity; aResult: string);
end;
TValidator<I: IModelEntity> = class(TInterfacedObject, IValidator)
private
protected
procedure DoValidate(aEntity: I; aResult: string); virtual; abstract;
public
procedure Validate(aEntity: IModelEntity; aResult: string);
end;
implementation
{ TValidator<T> }
procedure TValidator<I>.Validate(aEntity: IModelEntity; aResult: string);
begin
// The next line does not compiles
if not (aEntity is I) then
Exit;
DoValidate(aEntity as I, aResult);
end;
end.
Я добавляю комментарий к строке, которая не компилируется. Теперь, очевидно, что для универсального типа «I» должен быть определен GUID, чтобы это работало, однако нет способа указать это требование в качестве ограничения.
Возможным обходным решением может быть отказ от использования общего абстрактного класса и приведения интерфейса в валидаторе, но мне просто интересно, есть ли у кого-нибудь идея, как это сделать без приведения.
Комментарии:
1. Чтобы это сработало, я думаю, должен быть какой-то способ указать ограничение has GUID .
Ответ №1:
Кажется, работает следующее:
uses
SysUtils, TypInfo;
{ TValidator<I> }
procedure TValidator<I>.Validate(const aEntity: IModelEntity; aResult: string);
var
intf: I;
begin
if not Supports(aEntity, GetTypeData(TypeInfo(I))^.Guid, intf) then
Exit;
DoValidate(intf, aResult);
end;
Комментарии:
1. Какой режим сбоя, когда
I
у него нет GUID?2. Идентификатор GUID равен нулю ({00000000-0000-0000-0000-000000000000}), но, что интересно, поддержка по-прежнему возвращает True.
3. Можно только догадываться о том, что появится
intf
в этом случае.4. Мне пришлось бы пройти через код RTL, чтобы быть уверенным, но я бы предположил, что запрос нулевого GUID вернет указатель общего
IInterface/IUnknown
интерфейса, если вообще что-нибудь.