2016-04-23 27 views
1

Ich versuche, eine Spezifikation Dienstprogrammbibliothek zu schreiben.Delphi Rtiti Get Property - Warum ergibt dies AV?

Eine der Spezifikationen ist eine TExpressionSpecification. Im Grunde wird das Spezifikationsmuster implementiert, indem eine innere TExpression ausgewertet wird.

Einer der TExpression ist ein TPropertyExpression. Es ist einfach ein Ausdruck, der den Wert einer Eigenschaft durch ihren Namen mit Rtti bekommt.

Ich implementierte es auf die einfachste Weise, die ich könnte, aber kann wirklich nicht verstehen, warum es ein AV auf mich wirft.

Ich trat mit dem Debugger Thoroughly. Alle Arten sind, was sie sein sollen. Ich weiß nicht, warum der TRttiProperty.GetValue verheerend ist.

Kann jemand helfen? Einheit Spec;

interface 

uses 

Classes; 

type 

TPropertyExpression<TObjectType, TResultType> = class 

private 
    FPropertyName: string; 
public 
    constructor Create(aPropertyName: string); reintroduce; 
    function Evaluate(aObject: TObjectType): TResultType; 
    property PropertyName: string read FPropertyName write FPropertyName; 
end; 

procedure TestIt; 

implementation 

uses 

Rtti; 

constructor TPropertyExpression<TObjectType, TResultType>.Create(aPropertyName: 
    string); 
begin 
    inherited Create; 
    PropertyName := aPropertyName; 
end; 

function TPropertyExpression<TObjectType, TResultType>.Evaluate(aObject: 
    TObjectType): TResultType; 
var 
    aCtx : TRttiContext; 
    aModelType : TRttiType; 
    aResultType : TRttiType; 
    aProperty : TRttiProperty; 
    aValue : TValue; 
begin 
    aCtx := TRttiContext.Create; 
    aModelType := aCtx.GetType(System.TypeInfo(TObjectType)); 
    aResultType := aCtx.GetType(System.TypeInfo(TResultType)); 
    aProperty := aModelType.GetProperty(PropertyName); 
    aValue := aProperty.GetValue(Addr(aObject)); 
    Result := aValue.AsType<TResultType>; 
end; 

procedure TestIt; 
var 
    aComponent : TComponent; 
    aSpec : TPropertyExpression<TComponent, string>; 
begin 
    aComponent := TComponent.Create(nil); 
    aComponent.Name := 'ABC'; 
    aSpec := TPropertyExpression<TComponent, string>.Create('Name'); 
    WriteLn(aSpec.Evaluate(aComponent)); 
    Readln; 
end; 

end. 

Antwort

5

GetValue erwartet, dass der Instanz-Zeiger (aObject), aber Sie ihm die Adresse der Zeigervariable (@aObject) sind vorbei.

Constrain Ihre TObjectType zu einem Klasse-Typ:

type 
    TPropertyExpression<TObjectType: class; TResultType> = class... 

Dann statt Addr(aObject), direkt die Instanz übergeben:

aValue := aProperty.GetValue(Pointer(aObject)); 
+0

Es gibt keine Notwendigkeit, 'Pointer' goss Typen, nur passieren der Objektzeiger wie-ist: 'GetValue (aObject)'. –

+0

Denken Sie auch daran, dass Datensätze auch Eigenschaften haben können, also sollten Sie keine 'class' Einschränkung verwenden. Es sei denn, Sie definieren eine separate Ausdrucksklasse, um Datensatzinstanzen getrennt von Klasseninstanzen zu behandeln. –

+0

Ohne die Besetzung, bekomme ich E2010 Inkompatible Typen: 'Zeiger' und 'TObjectType'. Und während Sie Recht haben, dass Datensätze Eigenschaften haben können, wird RTTI für Datensatzeigenschaften nicht unterstützt. Also habe ich einfach angenommen, dass es in Ordnung ist, nur auf Klassen zu beschränken. –