How do I test a generic type variable for equality

2020-03-03 05:13发布

问题:

I'm trying to write a generic cached property accessor like the following but am getting a compiler error when trying to check whether the storage variable already contains a value:

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;

The error I'm getting is "E2015 Operator not applicable to this operand type".

Would I have to put a constraint on T to make this work? The help file says that Default() would accept anything except generic types. In my case I'm dealing mostly with simple types like String, Integer and TDateTime.

Or is there some other library function to perform this particular check?

I'm using Delphi 2009 in case that matters.


P.S.: Just in case it isn't clear from the code what I'm trying to do: In my case determining the actual property values might take a while for various reasons and sometimes I might not even need them at all. On the plus side however the values are constant so I only want to call the code that determines the actual value the first time that property is accessed and then store the value in a class field and the next time that property is accessed return the cached value directly. Here's an example of how I hoped I would be able to use that 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;

(obviously, there's more than just one property)

回答1:

After a hint in the comments from Binis and digging around a little in Generics.Collections I came up with the following which appears to work just as I wanted it:

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:

As I understand things it looks like u want to do some kind of memoize functionality. If this is the case just read this article

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



回答3:

The problem is not the Default function, but the equality operator =.

You could constrain T to IEquatable and use the Equals method like this:

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;