2009-11-23 3 views
5

Je suis en train d'écrire un accesseur de propriété en cache générique comme ce qui suit, mais je reçois une erreur de compilation lorsque vous essayez de vérifier si la variable de stockage contient déjà une valeur:Comment tester une variable de type générique pour l'égalité avec Default (T) dans 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; 

L'erreur que je reçois est " E2015 Opérateur non applicable à ce type d'opérande ".

Est-ce que je dois mettre une contrainte sur T pour faire ce travail? Le fichier d'aide indique que Default() accepterait tout sauf les types génériques. Dans mon cas, je traite principalement avec des types simples comme String, Integer et TDateTime.

Ou est-il une autre fonction de bibliothèque pour effectuer cette vérification particulière?

J'utilise Delphi 2009 dans l'affaire qui compte.


PS: Juste au cas où il ne ressort pas du code ce que je suis en train de faire: Dans mon cas, la détermination des valeurs de propriété réelle peut prendre un certain temps pour diverses raisons et parfois je pourrais même pas besoin eux du tout. Du côté positif, cependant, les valeurs sont constantes, donc je veux seulement appeler le code qui détermine la valeur réelle la première fois que cette propriété est accédée, puis stocker la valeur dans un champ de classe et la prochaine fois que cette propriété est retournée. directement. Voici un exemple de la façon dont j'espérais pouvoir utiliser ce code:

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; 

(de toute évidence, il y a plus d'une propriété)

+0

+1 bonne idée de la manière. – jpfollenius

+1

Vous devez probablement utiliser un type nullable, sinon le code réévaluera constamment les propriétés qui sont déjà mises en cache mais qui ont la valeur par défaut. Voir par exemple http://blogs.embarcadero.com/abauer/2008/09/18/38869 – mghie

+0

@mghie: Bon point et d'habitude je suis d'accord, mais dans ce cas particulier, la valeur par défaut peut en toute sécurité toujours être interprété comme « non initialisée » . –

Répondre

10

Après une touche dans les commentaires de Binis et creuser autour d'un peu Generics.Collections je suis venu avec ce qui suit, qui semble fonctionner comme je le voulais :

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

Il est à noter que" IEqualityComparer "est maintenant dans Generics.Defaults (pas sûr depuis quand, en utilisant Berlin 10.1) – Zulukas

-1

Le problème n'est pas la fonction Default, mais l'opérateur d'égalité = .

Vous pouvez limiter T-IEquatable et utiliser la méthode Equals comme ceci:

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

Je ne sais pas pourquoi, mais je reçois un "identificateur Undeclared'equitable" "avec ce code (même si je peux voir' IEquatable' est en effet déclaré dans System.pas). Je doute cependant cela fonctionnerait pour moi de toute façon que je ne suis pas au courant que les types primitifs comme 'CHAINE',' 'TDateTime' ou Integer' en quelque sorte soutiennent implicitement cette interface ... –

+0

@Oliver: vous avez raison, vous pouvez » t l'utilise pour les types primitifs. Malheureusement, je ne suis pas au courant d'une autre solution. Voyons voir si les autres sont capables d'aider. Je ne sais pas pourquoi vous obtenez un message d'erreur lors de l'utilisation de 'IEquatable'. – jpfollenius

+0

En fait, ce qui est déclaré dans System.pas n'est pas 'IEquatable' mais' IEquatable 'ce qui explique l'erreur ...' GetProp > (... 'compile mais ensuite, comme prévu, je reçois l'erreur lors du passage de 'String' pour' T': "Le paramètre de type 'T' doit supporter l'interface IEquatable " –

1

Comme je comprends les choses, il semble que tu veux faire une sorte de fonctionnalité memoize. Si tel est le cas de lire cet article

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

+0

Merci, c'est en fait très similaire à ce que j'essaie de faire mais la mise en oeuvre de Craig aboutirait Dans mon cas particulier, il y aurait trop de frais inutiles car cela instancierait une nouvelle instance 'TDictionary' pour chaque propriété ... Ce n'est malheureusement pas non plus une réponse à la question initiale de savoir si une variable générique a une valeur non-par défaut ou pas, donc vu ce contexte, je crains de ne pas pouvoir même augmenter votre réponse. :( –

+0

Vous pouvez éviter cette surcharge en utilisant quelque chose comme un 'TCacheManager' Le gestionnaire de cache a un dictionnaire y. Utilisez simplement quelque chose comme 'IntToStr (Integer (Pointer (Self))) + '.Property1'' comme clé et enregistrez vos valeurs en cache. – jpfollenius

+3

La seule façon "légale" de comparer les génériques est de faire ce que fait Delphi dans Generics.Collections.pas. Jetez un coup d'œil à l'implémentation QuickSort. – Binis

Questions connexes