返回属性值的类的Delphi静态方法

返回属性值的类的Delphi静态方法,delphi,properties,static-methods,Delphi,Properties,Static Methods,我正在制作一个Delphi VCL应用程序。有一个类TStudent,其中我有两个静态函数:一个从TStudent数组返回姓氏,另一个返回学生的名字。他们的代码类似于: class function TStudent.FirstNameOf(aLastName: string): string; var i : integer; begin for i := 0 to Length(studentsArray) - 1 do begin if studentsArray[i].Las

我正在制作一个Delphi VCL应用程序。有一个类TStudent,其中我有两个静态函数:一个从TStudent数组返回姓氏,另一个返回学生的名字。他们的代码类似于:

class function TStudent.FirstNameOf(aLastName: string): string;
var i : integer;
begin
  for i := 0 to Length(studentsArray) - 1 do begin
    if studentsArray[i].LastName = aLastName then
    begin
       result := studentsArray[i].FirstName;
       Exit;
    end;
  end;
  result := 'no match was found';
end;

class function TStudent.LastNameOf(aFirstName: string): string;
var i : integer;
begin
  for i := 0 to Length(studentsArray) - 1 do begin
    if studentsArray[i].FirstName = aFirstName then
    begin
       result := studentsArray[i].LastName;
       Exit;
    end;
  end;
  result := 'no match was found';
end;

我的问题是如何避免几乎相同的代码编写两次。是否有任何方法可以将属性作为函数的参数传递。

对于此线性搜索,可以使用带有变量捕获的匿名方法。这种方法使谓词具有完全的通用性。您可以测试任何类型的任何字段是否相等。您可以测试更复杂的谓词,例如非此即彼或检查

代码可能如下所示:

class function TStudent.LinearSearch(const IsMatch: TPredicate<TStudent>; 
  out Index: Integer): Boolean;
var
  i: Integer;
begin
  for i := low(studentsArray) to high(studentsArray) do 
  begin
    if IsMatch(studentsArray[i]) then
    begin
      Index := i;
      Result := True;
      exit;
    end;
  end;

  Index := -1;
  Result := False;
end;
class function TStudent.GetFirstName(const LastName: string): string;
var 
  Index: Integer;
  IsMatch: TPredicate<TStudent>;
begin
  IsMatch := 
    function(Student: TStudent): Boolean
    begin
      Result := Student.LastName=LastName;
    end;

  if not LinearSearch(IsMatch, Index) then
  begin
    raise ...
  end;
  Result := studentsArray[Index].FirstName;
end;
FirstName := TStudent.GetNameOf('LastName', 'Smoe', 'FirstName');
因此,您可以这样编写方法:

class function TStudent.LinearSearch(const IsMatch: TPredicate<TStudent>; 
  out Index: Integer): Boolean;
var
  i: Integer;
begin
  for i := low(studentsArray) to high(studentsArray) do 
  begin
    if IsMatch(studentsArray[i]) then
    begin
      Index := i;
      Result := True;
      exit;
    end;
  end;

  Index := -1;
  Result := False;
end;
class function TStudent.GetFirstName(const LastName: string): string;
var 
  Index: Integer;
  IsMatch: TPredicate<TStudent>;
begin
  IsMatch := 
    function(Student: TStudent): Boolean
    begin
      Result := Student.LastName=LastName;
    end;

  if not LinearSearch(IsMatch, Index) then
  begin
    raise ...
  end;
  Result := studentsArray[Index].FirstName;
end;
FirstName := TStudent.GetNameOf('LastName', 'Smoe', 'FirstName');
类函数TStudent.GetFirstName(constlastname:string):string;
变量
索引:整数;
IsMatch:t预测;
开始
IsMatch:=
函数(学生:TStudent):布尔值
开始
结果:=Student.LastName=LastName;
终止
如果不是线性搜索(IsMatch,索引),则
开始
提升
终止
结果:=studentsArray[Index].FirstName;
终止
同样,对于
GetLastName


如果您的Delphi不支持匿名方法,那么您将无法使用变量捕获,并且必须使用对象的
方法类型找到更复杂的方法。但是,基本思路基本相同。

您可以使用带有变量捕获的匿名方法进行此线性搜索。这种方法使谓词具有完全的通用性。您可以测试任何类型的任何字段是否相等。您可以测试更复杂的谓词,例如非此即彼或检查

代码可能如下所示:

class function TStudent.LinearSearch(const IsMatch: TPredicate<TStudent>; 
  out Index: Integer): Boolean;
var
  i: Integer;
begin
  for i := low(studentsArray) to high(studentsArray) do 
  begin
    if IsMatch(studentsArray[i]) then
    begin
      Index := i;
      Result := True;
      exit;
    end;
  end;

  Index := -1;
  Result := False;
end;
class function TStudent.GetFirstName(const LastName: string): string;
var 
  Index: Integer;
  IsMatch: TPredicate<TStudent>;
begin
  IsMatch := 
    function(Student: TStudent): Boolean
    begin
      Result := Student.LastName=LastName;
    end;

  if not LinearSearch(IsMatch, Index) then
  begin
    raise ...
  end;
  Result := studentsArray[Index].FirstName;
end;
FirstName := TStudent.GetNameOf('LastName', 'Smoe', 'FirstName');
因此,您可以这样编写方法:

class function TStudent.LinearSearch(const IsMatch: TPredicate<TStudent>; 
  out Index: Integer): Boolean;
var
  i: Integer;
begin
  for i := low(studentsArray) to high(studentsArray) do 
  begin
    if IsMatch(studentsArray[i]) then
    begin
      Index := i;
      Result := True;
      exit;
    end;
  end;

  Index := -1;
  Result := False;
end;
class function TStudent.GetFirstName(const LastName: string): string;
var 
  Index: Integer;
  IsMatch: TPredicate<TStudent>;
begin
  IsMatch := 
    function(Student: TStudent): Boolean
    begin
      Result := Student.LastName=LastName;
    end;

  if not LinearSearch(IsMatch, Index) then
  begin
    raise ...
  end;
  Result := studentsArray[Index].FirstName;
end;
FirstName := TStudent.GetNameOf('LastName', 'Smoe', 'FirstName');
类函数TStudent.GetFirstName(constlastname:string):string;
变量
索引:整数;
IsMatch:t预测;
开始
IsMatch:=
函数(学生:TStudent):布尔值
开始
结果:=Student.LastName=LastName;
终止
如果不是线性搜索(IsMatch,索引),则
开始
提升
终止
结果:=studentsArray[Index].FirstName;
终止
同样,对于
GetLastName


如果您的Delphi不支持匿名方法,那么您将无法使用变量捕获,并且必须使用对象的
方法类型找到更复杂的方法。不过,基本思路大致相同。

我还没有测试过,但我相信这可能是一个解决方案

uses TypInfo;

class function TStudent.GetProperty( propertyName: string, searchValue : Variant ) : Variant ;
var i : integer;
begin
  for i := 0 to Length(studentsArray) - 1 do begin
    if GetPropValue( studentsArray[i], propertyName ) = searchValue 
       result :=  GetPropValue( studentsArray[i], propertyName );
  end;
  // your code in case of not finding anything

end;

我还没有测试过,但我相信这可能是一个解决方案

uses TypInfo;

class function TStudent.GetProperty( propertyName: string, searchValue : Variant ) : Variant ;
var i : integer;
begin
  for i := 0 to Length(studentsArray) - 1 do begin
    if GetPropValue( studentsArray[i], propertyName ) = searchValue 
       result :=  GetPropValue( studentsArray[i], propertyName );
  end;
  // your code in case of not finding anything

end;

与常规数组属性一样,您可以将多个属性与由单个getter函数支持的
索引
说明符一起使用:

  TDefault = class(TObject)
  private
    class function GetProp(const FindWhat: string; FindWhere: Integer): string;
        static;
  protected
    /// <remarks>
    ///   You don't really need this one. I've added it for an illustration
    ///   purposes.
    /// </remarks>
    class property Prop[const FindWhat: string; FindWhere: Integer]: string read GetProp;
  public
    class property A[const FindWhat: string]: string index 0 read GetProp;
    class property B[const FindWhat: string]: string index 1 read GetProp;
  end;

{ ... }

class function TDefault.GetProp(const FindWhat: string; FindWhere: Integer): string;
begin
  case FindWhere of
    0: Result := 'Hallo!';
    1: Result := 'Hello!';
  end;
  Result := Result + ' ' + Format('searching for "%s"', [FindWhat]);
end;
TDefault=class(TObject)
私有的
类函数GetProp(const FindWhat:string;FindWhere:Integer):string;
静止的
受保护的
/// 
///你真的不需要这个。我加上它是为了举例说明
///目的。
/// 
类属性Prop[const FindWhat:string;FindWhere:Integer]:string read GetProp;
平民的
类属性A[const FindWhat:string]:字符串索引0读取GetProp;
类属性B[const FindWhat:string]:字符串索引1读取GetProp;
终止
{ ... }
类函数TDefault.GetProp(const FindWhat:string;FindWhere:Integer):string;
开始
在哪里找到的案例
0:结果:='Hallo!';
1:结果:=“你好!”;
终止
结果:=结果+“”+格式('搜索“%s”,[FindWhat]);
终止
如您所见,类属性与实例属性完全相同


我必须说,在属性getter中执行搜索是一个非常糟糕的主意。

您可以使用带有
索引的多个属性
说明符,由单个getter函数支持,就像您对常规数组属性所做的那样:

  TDefault = class(TObject)
  private
    class function GetProp(const FindWhat: string; FindWhere: Integer): string;
        static;
  protected
    /// <remarks>
    ///   You don't really need this one. I've added it for an illustration
    ///   purposes.
    /// </remarks>
    class property Prop[const FindWhat: string; FindWhere: Integer]: string read GetProp;
  public
    class property A[const FindWhat: string]: string index 0 read GetProp;
    class property B[const FindWhat: string]: string index 1 read GetProp;
  end;

{ ... }

class function TDefault.GetProp(const FindWhat: string; FindWhere: Integer): string;
begin
  case FindWhere of
    0: Result := 'Hallo!';
    1: Result := 'Hello!';
  end;
  Result := Result + ' ' + Format('searching for "%s"', [FindWhat]);
end;
TDefault=class(TObject)
私有的
类函数GetProp(const FindWhat:string;FindWhere:Integer):string;
静止的
受保护的
/// 
///你真的不需要这个。我加上它是为了举例说明
///目的。
/// 
类属性Prop[const FindWhat:string;FindWhere:Integer]:string read GetProp;
平民的
类属性A[const FindWhat:string]:字符串索引0读取GetProp;
类属性B[const FindWhat:string]:字符串索引1读取GetProp;
终止
{ ... }
类函数TDefault.GetProp(const FindWhat:string;FindWhere:Integer):string;
开始
在哪里找到的案例
0:结果:='Hallo!';
1:结果:=“你好!”;
终止
结果:=结果+“”+格式('搜索“%s”,[FindWhat]);
终止
如您所见,类属性与实例属性完全相同


我必须说,在属性getter中执行搜索是一个非常糟糕的主意。

如果您使用的是Delphi 2010或更高版本,您可以使用扩展RTTI:

uses
  ..., Rtti;

type
  TStudent = class
  public
    FirstName: String;
    LastName: String;

    class function GetNameOf(const aFieldToFind, aNameToFind, aFieldToReturn: string): string;
   end;

class function TStudent.GetNameOf(const aFieldToFind, aNameToFind, aFieldToReturn: string): string;
var
  i : integer;
  ctx: TRttiContent;
  StudentType: TRttiType;
  Field: TRttiField;
  Value: TValue;
begin
  ctx := TRttiContext.Create;
  StudentType := ctx.GetType(TStudent);
  Field := StudentType.GetField(aFieldToFind);

  for i := 0 to Length(studentsArray) - 1 do
  begin
    if Field.GetValue(@studentsArray[i]).AsString = aNameToFind then
    begin
      Result := StudentType.GetField(aFieldToReturn).GetValue(@studentsArray[i]).AsString;
      Exit;
    end;
  end;
  Result := 'no match was found';
end;
然后你可以这样称呼它:

class function TStudent.LinearSearch(const IsMatch: TPredicate<TStudent>; 
  out Index: Integer): Boolean;
var
  i: Integer;
begin
  for i := low(studentsArray) to high(studentsArray) do 
  begin
    if IsMatch(studentsArray[i]) then
    begin
      Index := i;
      Result := True;
      exit;
    end;
  end;

  Index := -1;
  Result := False;
end;
class function TStudent.GetFirstName(const LastName: string): string;
var 
  Index: Integer;
  IsMatch: TPredicate<TStudent>;
begin
  IsMatch := 
    function(Student: TStudent): Boolean
    begin
      Result := Student.LastName=LastName;
    end;

  if not LinearSearch(IsMatch, Index) then
  begin
    raise ...
  end;
  Result := studentsArray[Index].FirstName;
end;
FirstName := TStudent.GetNameOf('LastName', 'Smoe', 'FirstName');


如果您使用的是Delphi 2010或更高版本,则可以使用扩展RTTI:

uses
  ..., Rtti;

type
  TStudent = class
  public
    FirstName: String;
    LastName: String;

    class function GetNameOf(const aFieldToFind, aNameToFind, aFieldToReturn: string): string;
   end;

class function TStudent.GetNameOf(const aFieldToFind, aNameToFind, aFieldToReturn: string): string;
var
  i : integer;
  ctx: TRttiContent;
  StudentType: TRttiType;
  Field: TRttiField;
  Value: TValue;
begin
  ctx := TRttiContext.Create;
  StudentType := ctx.GetType(TStudent);
  Field := StudentType.GetField(aFieldToFind);

  for i := 0 to Length(studentsArray) - 1 do
  begin
    if Field.GetValue(@studentsArray[i]).AsString = aNameToFind then
    begin
      Result := StudentType.GetField(aFieldToReturn).GetValue(@studentsArray[i]).AsString;
      Exit;
    end;
  end;
  Result := 'no match was found';
end;
然后你可以这样称呼它:

class function TStudent.LinearSearch(const IsMatch: TPredicate<TStudent>; 
  out Index: Integer): Boolean;
var
  i: Integer;
begin
  for i := low(studentsArray) to high(studentsArray) do 
  begin
    if IsMatch(studentsArray[i]) then
    begin
      Index := i;
      Result := True;
      exit;
    end;
  end;

  Index := -1;
  Result := False;
end;
class function TStudent.GetFirstName(const LastName: string): string;
var 
  Index: Integer;
  IsMatch: TPredicate<TStudent>;
begin
  IsMatch := 
    function(Student: TStudent): Boolean
    begin
      Result := Student.LastName=LastName;
    end;

  if not LinearSearch(IsMatch, Index) then
  begin
    raise ...
  end;
  Result := studentsArray[Index].FirstName;
end;
FirstName := TStudent.GetNameOf('LastName', 'Smoe', 'FirstName');


如果您对
t学生
记录进行一点重组,一切都会变得简单。与其有多个名称不同的字符串字段,不如声明具有枚举范围的字符串数组

为枚举指定有意义的名称,并添加一个搜索函数,可在其中指定搜索字段和结果字段

Type   
  TStudentField = (sfFirstName,sfLastName);  // Helper enumeration type

  TStudent = record
    Field: array[TStudentField] of String;
    class function SearchNameOf(searchField: TStudentField; 
      const aSearchName: string; resultField: TStudentField): string; static;
  end;
下面是一个测试示例:

program ProjectTest;

{$APPTYPE CONSOLE}

Type   
  TStudentField = (sfFirstName,sfLastName);

  TStudent = record
    Field: array[TStudentField] of String;
    class function SearchNameOf(searchField: TStudentField; const aSearchName: string; resultField: TStudentField): string; static;
  end;

var
  studentsArray : array of TStudent;

class function TStudent.SearchNameOf(searchField: TStudentField; const aSearchName: string; resultField: TStudentField): string;
var
  i : integer;
begin
  for i := 0 to Length(studentsArray) - 1 do begin
    if (studentsArray[i].Field[searchField] = aSearchName) then
    begin
      Result := studentsArray[i].Field[resultField];
      Exit;
    end;
  end;
  result := 'no match was found';
end;

begin
  SetLength(studentsArray,2);
  studentsArray[0].Field[sfFirstName] := 'Buzz';
  studentsArray[0].Field[sfLastName] := 'Aldrin';
  studentsArray[1].Field[sfFirstName] := 'Neil';
  studentsArray[1].Field[sfLastName] := 'Armstrong';
  WriteLn(TStudent.SearchNameOf(sfFirstName,'Neil',sfLastName));
  ReadLn;
end.

如果您对
t学生
记录进行一点重组,一切都会变得简单。与其有多个名称不同的字符串字段,不如声明具有枚举范围的字符串数组

为枚举指定有意义的名称,并添加一个搜索函数,可在其中指定搜索字段和结果字段

Type   
  TStudentField = (sfFirstName,sfLastName);  // Helper enumeration type

  TStudent = record
    Field: array[TStudentField] of String;
    class function SearchNameOf(searchField: TStudentField; 
      const aSearchName: string; resultField: TStudentField): string; static;
  end;
下面是一个测试示例:

program ProjectTest;

{$APPTYPE CONSOLE}

Type   
  TStudentField = (sfFirstName,sfLastName);

  TStudent = record
    Field: array[TStudentField] of String;
    class function SearchNameOf(searchField: TStudentField; const aSearchName: string; resultField: TStudentField): string; static;
  end;

var
  studentsArray : array of TStudent;

class function TStudent.SearchNameOf(searchField: TStudentField; const aSearchName: string; resultField: TStudentField): string;
var
  i : integer;
begin
  for i := 0 to Length(studentsArray) - 1 do begin
    if (studentsArray[i].Field[searchField] = aSearchName) then
    begin
      Result := studentsArray[i].Field[resultField];
      Exit;
    end;
  end;
  result := 'no match was found';
end;

begin
  SetLength(studentsArray,2);
  studentsArray[0].Field[sfFirstName] := 'Buzz';
  studentsArray[0].Field[sfLastName] := 'Aldrin';
  studentsArray[1].Field[sfFirstName] := 'Neil';
  studentsArray[1].Field[sfLastName] := 'Armstrong';
  WriteLn(TStudent.SearchNameOf(sfFirstName,'Neil',sfLastName));
  ReadLn;
end.

您如何知道在使用这些函数时将返回哪个学生的姓名?为什么要硬编码要查找的姓名以及重复的姓名呢?我有一个