2009-11-23 12 views
5

estoy tratando de escribir una propiedad de acceso en caché genérico como el siguiente, pero estoy recibiendo un error de compilación cuando se trata de comprobar si la variable de almacenamiento ya contiene un valor:¿Cómo puedo probar una variable de tipo genérico para igualdad con Predeterminado (T) en 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; 

El error que estoy recibiendo es " Operador E2015 no aplicable a este tipo de operando ".

¿Debo poner una restricción en T para que funcione? El archivo de ayuda dice que Default() aceptaría cualquier cosa excepto los tipos genéricos. En mi caso, trato principalmente con tipos simples como String, Integer y TDateTime.

¿O hay alguna otra función de biblioteca para realizar esta comprobación en particular?

Estoy usando Delphi 2009 en caso de que eso importe.


PD: Sólo en caso de que no está claro a partir del código de lo que estoy tratando de hacer: En mi caso, la determinación de los valores de las propiedades reales podría tomar un tiempo, por diversas razones ya veces ni siquiera podría necesitar ellos en absoluto. Sin embargo, los valores son constantes, así que solo quiero llamar al código que determina el valor real la primera vez que se accede a esa propiedad y luego almacenar el valor en un campo de clase y la próxima vez que se acceda a esa propiedad devuelva el valor almacenado directamente. Aquí está un ejemplo de cómo Tenía la esperanza de ser capaz de utilizar ese código:

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; 

(obviamente, no hay más que una sola propiedad)

+0

+1 buena idea por cierto. – jpfollenius

+1

Probablemente deberías usar un tipo anulable, de lo contrario el código reevaluaría constantemente las propiedades que ya están en la memoria caché pero que tienen el valor predeterminado. Ver por ejemplo http://blogs.embarcadero.com/abauer/2008/09/18/38869 – mghie

+0

@mghie: Buen punto y por lo general estoy de acuerdo, pero en este caso particular, el valor predeterminado siempre se puede interpretar como "sin inicializar" . –

Respuesta

10

Después de una insinuación en los comentarios de Binis y escarbar un poco en Generics.Collections me ocurrió lo siguiente, que parece funcionar igual que lo quería :

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

Vale la pena señalar que" IEqualityComparer "está ahora dentro de Generics.Defaults (no estoy seguro desde cuándo, con Berlin 10.1) – Zulukas

-1

El problema no es la función Default, pero el operador de igualdad = .

Se podría limitar T-IEquatable y utilizar el método Equals así:

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

No estoy seguro de por qué pero obtengo un "identificador no declarado 'IEquatable'" con ese código (aunque puedo ver que 'IEquatable' está declarado en System.pas). Sin embargo, dudaría que esto funcione para mí de todos modos, ya que no soy consciente de que los tipos primitivos como 'String',' TDateTime' o 'Integer' de alguna manera implícitamente admiten esa interfaz ... –

+0

@Oliver: tienes razón, puedes ' t usar eso para tipos primitivos. Lamentablemente, no tengo conocimiento de otra solución. Veamos si otros pueden ayudar. No sé por qué aparece un mensaje de error cuando se usa 'IEquatable'. – jpfollenius

+0

En realidad, lo que se declara en System.pas no es 'IEquatable' sino' IEtabletable 'que explica el error ...' GetProp > (... 'compila pero luego, como esperaba, obtengo el error al pasar 'String' para' T': "Tipo de parámetro 'T' debe ser compatible con la interfaz IEquatable " –

1

Según entiendo las cosas parece que quieres hacer algún tipo de funcionalidad memoize. Si este es el caso que acabamos de leer este artículo

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

+0

¡Gracias! Esto es de hecho muy similar a lo que intento hacer, pero la implementación de Craig resultaría en una sobrecarga innecesaria en mi caso particular porque crearía una instancia nueva de 'TDictionary' para cada propiedad ... Desafortunadamente, tampoco es una respuesta a la pregunta original de cómo probar si una variable genéricamente tipada tiene un valor no predeterminado o no, así que teniendo en cuenta ese contexto, me temo que no podría ni siquiera renunciar a su respuesta. :( –

+0

Puede evitar esta sobrecarga usando algo como un 'TCacheManager'. El administrador de caché tiene un diccionario y Simplemente use algo como 'IntToStr (Integer (Pointer (Self))) + '.Property1'' como la clave y registre sus valores en caché. – jpfollenius

+3

Bueno, la única forma "legal" de comparar genéricos es hacer lo que hace Delphi en Generics.Collections.pas. Eche un vistazo a la implementación de QuickSort. – Binis

Cuestiones relacionadas