2009-11-23 4 views
5

Ich versuche, eine generische gecached Eigenschaftenaccessor wie folgt zu schreiben, aber einen Compiler-Fehler bin immer wenn, ob die Speichergröße einen Wert enthält bereits zu überprüfen versuchen:Wie teste ich eine generische Typvariable für Gleichheit mit Default (T) in Delphi?

function TMyClass.GetProp<T>(var ADataValue: T; const ARetriever: TFunc<T>): T; 
begin 
    if ADataValue = Default(T) then // <-- compiler error on this line 
    ADataValue := ARetriever(); 
    Result := ADataValue; 
end; 

Der Fehler Ich erhalte ist " E2015 Operator nicht anwendbar auf diesen Operandentyp ".

Muss ich eine Einschränkung auf T setzen, damit dies funktioniert? Die Hilfedatei sagt, dass Default() alles außer generische Typen akzeptieren würde. In meinem Fall habe ich es hauptsächlich mit einfachen Typen wie String, Integer und TDateTime zu tun.

Oder gibt es eine andere Bibliotheksfunktion, um diese spezielle Prüfung durchzuführen?

Ich benutze Delphi 2009, falls das wichtig ist.


PS: Nur für den Fall ist es aus dem Code nicht klar, was ich zu tun werde versuchen: In meinem Fall der tatsächlichen Eigenschaftswerte bestimmen, könnte eine Weile aus verschiedenen Gründen nehmen und manchmal könnte ich nicht einmal brauchen sie überhaupt. Auf der positiven Seite jedoch sind die Werte konstant, so dass ich nur den Code aufrufen möchte, der den tatsächlichen Wert beim ersten Zugriff auf die Eigenschaft bestimmt, und dann den Wert in einem Klassenfeld speichern und beim nächsten Zugriff auf die Eigenschaft den zwischengespeicherten Wert zurückgeben direkt. Hier ist ein Beispiel dafür, wie ich hoffte, ich wäre in der Lage diesen Code zu verwenden:

type 
    TMyClass = class 
    private 
    FSomeProp: String; 
    function GetSomeProp: String; 

    function GetProp<T>(var ADataValue: T; const ARetriever: TFunc<T>): T; 
    public 
    property SomeProp read GetSomeProp; 
    end; 

function GetSomeProp: String; 
begin 
    Result := GetProp<String>(FSomeProp, 
          function: String 
          begin 
           Result := SomeSlowOrExpensiveCalculation; 
          end); 
end; 

(natürlich gibt es mehr als nur eine Eigenschaft)

+0

+1 nette Idee übrigens. – jpfollenius

+1

Sie sollten wahrscheinlich einen NULL-fähigen Typ verwenden, da andernfalls der Code ständig Eigenschaften neu bewertet, die bereits zwischengespeichert sind, aber den Standardwert haben. Siehe zum Beispiel http://blogs.embarcadero.com/abauer/2008/09/18/38869 – mghie

+0

@mghie: Guter Punkt und normalerweise würde ich zustimmen, aber in diesem speziellen Fall kann der Standardwert sicher immer als "nicht initialisiert" interpretiert werden . –

Antwort

10

Nach einem Hinweis in den Kommentaren von Binis und Graben um ein wenig in Generics.Collections kam ich mit dem folgenden auf die so wie ich es wollte, scheint zu funktionieren :

function TMyClass.GetProp<T>(var ADataValue: T; const ARetriever: TFunc<T>): T; 
var 
    lComparer: IEqualityComparer<T>; 
begin 
    lComparer := TEqualityComparer<T>.Default; 
    if lComparer.Equals(ADataValue, Default(T)) then 
    ADataValue := ARetriever(); 
    Result := ADataValue; 
end; 
+2

Es ist bemerkenswert, dass" IEqualityComparer "jetzt in Generics.Defaults (nicht sicher seit wann, mit Berlin 10.1) – Zulukas

-1

Das Problem ist nicht die Default Funktion, aber die Gleichheitsoperator = .

Sie könnten T-IEquatable beschränken und die Equals Methode wie folgt verwenden:

TMyClass = class 
    function GetProp<T : IEquatable<T>>(var ADataValue: T; const ARetriever: 
end; 
... 
function TMyClass.GetProp<T>(var ADataValue: T; const ARetriever: TFunc<T>): T; 
begin 
if ADataValue.Equals (Default(T)) then 
    ADataValue := ARetriever(); 
Result := ADataValue; 
end; 
+0

Nicht sicher warum, aber ich bekomme einen "Undeclared Bezeichner 'IEquatable'" mit diesem Code (obwohl ich sehen kann "IEquatable" ist tatsächlich in System.pas deklariert). Ich würde jedoch bezweifeln, dass dies für mich sowieso funktionieren würde, da ich nicht weiß, dass primitive Typen wie "String", "TDateTime" oder "Integer" diese Schnittstelle irgendwie implizit unterstützen ... –

+0

@Oliver: Du hast Recht, du kannst ' t das für primitive Typen verwenden. Leider ist mir keine andere Lösung bekannt. Mal sehen, ob andere helfen können. Ich weiß nicht, warum Sie eine Fehlermeldung erhalten, wenn Sie 'IEquatable' verwenden. – jpfollenius

+0

Eigentlich ist das, was in System.pas deklariert wird, nicht "IEquatable", sondern "IEquatable ", was den Fehler erklärt ... 'GetProp > (...' compiliert aber dann, wie erwartet, bekomme ich der Fehler beim Übergeben von 'String' für' T': "Typ-Parameter 'T' muss Schnittstelle IEquatable " –

1

Wie ich die Dinge verstehen, es sieht aus wie u irgendeine Art von memoize Funktionalität machen wollen. Wenn dies der Fall liest gerade diesen Artikel

http://blogs.teamb.com/craigstuntz/2008/10/01/37839/

+0

Danke! Das ist in der Tat sehr ähnlich zu dem, was ich versuche, aber Craigs Umsetzung würde dazu führen zu viel unnötiger Overhead in meinem speziellen Fall, weil es eine neue "TDictionary" -Instanz für jede Eigenschaft instanziiert ... Es ist leider auch keine Antwort auf die ursprüngliche Frage, wie man testet, ob eine generisch typisierte Variable einen nicht-voreingestellten Wert hat oder nicht, so dass ich angesichts dieses Kontexts nicht einmal deine Antwort verbessern konnte :( –

+0

Du könntest diesen Overhead vermeiden, indem du etwas wie einen 'TCacheManager' verwendest. Der Cache-Manager hat ein Dictionar y. Verwenden Sie einfach so etwas wie 'IntToStr (Ganzzahl (Zeiger (Selbst))) + '.Property1'' als Schlüssel und registrieren Sie Ihre zwischengespeicherten Werte. – jpfollenius

+3

Nun, der einzige "legale" Weg, Generika zu vergleichen, ist zu tun, was Delphi in Generics.Collections.pas tut. Sehen Sie sich die QuickSort-Implementierung an. – Binis