数组类型可以有方法吗?

6

我定义了一个动态数组类型如下:

TMyIntegerArray = array of integer:

我想使用一个IndexOf函数,就像我处理TObject的派生类一样:

var
  MyArray : TMyIntegerArray;
  i : integer:
begin
  //...
  i := MyArray.IndexOf(10);
  //...
end;

目前,我找到的唯一解决方法是编写一个接受数组和目标值作为参数的函数:

function IndexOf(AArray : TMyIntegerArray; ATargetValue : integer; AOffset : integer = 0);
begin
  Result := AOffset;
  while(Result < Length(AArray)) do
  begin
    if(AArray[Result] = ATargetValue)
    then Exit;
    Result := Result + 1;
  end;
  Result := -1;
end;

能否为TMyIntegerArray类型添加类似于IndexOf的函数?

更多信息:

目前,我正在使用Delphi2007,但我也想知道是否有办法在新版本的Delphi中为数组类型添加方法。


我的建议是将你的数组放在一个记录中。这样不仅可以给它添加方法,而且你还可以利用作用域规则来帮助正确封装内部结构。 - Disillusioned
3个回答

16
在Delphi的更新版本(XE3+)中,可以使用record helpers来实现对数组类型的方法:
program ProjectTest;

{$APPTYPE CONSOLE}

Type
  TMyArray = array of integer;

  TMyArrayHelper = record helper for TMyArray
    procedure Print;
    function IndexOf(ATargetValue : integer; AOffset : integer = 0): Integer;
  end;

procedure TMyArrayHelper.Print;
var
  i: Integer;
begin
  for i in Self do WriteLn(i);  // Use Self for variable reference
end;

function TMyArrayHelper.IndexOf(ATargetValue : integer; AOffset : integer = 0): Integer;
begin
  Result := AOffset;
  while(Result < Length(Self)) do
  begin
    if(Self[Result] = ATargetValue)
    then Exit;
    Result := Result + 1;
  end;
  Result := -1;
end;

var
  myArr : TMyArray;
begin
  myArr := [0,1,2];  // A neat way to populate a dynamic array (XE7+)
  myArr.Print;
  WriteLn(myArr.IndexOf(2));

  ReadLn;
end.
注意:您可以跳过TMyArray类型声明,使用TArray<Integer>以获得更宽松的类型解析。与记录辅助程序一样,一个类型只能有一个附加的辅助程序(将使用最近的作用域中的辅助程序)。

这种类型的辅助程序称为内置类型辅助程序,其中编译器在类型周围放置一个隐式记录结构。


3
嗯,我不知道那个。谢谢。 - David Heffernan

6

虽然LU RD为您的问题提供了直接的解决方案,但我将基于泛型添加略有不同的方法。这样做的优点是可以在一个地方为不同的数组类型提供有效的解决方案。

对于支持泛型的Delphi版本,可以采用System.Generics.Collections中的TArray中使用的方式。这是该类的直接扩展,引入了一个IndexOf函数:

type
  TArrayExt = class(TArray)
  public
    class function IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>; Index, Count:
        Integer): Integer; overload; static;
    class function IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>): Integer; overload;
        static;
    class function IndexOf<T>(const Values: array of T; const Item: T): Integer; overload; static;
  end;

class function TArrayExt.IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>; Index,
    Count: Integer): Integer;
var
  I: Integer;
begin
  if (Index < Low(Values)) or ((Index > High(Values)) and (Count > 0))
    or (Index + Count - 1 > High(Values)) or (Count < 0)
    or (Index + Count < 0) then
    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
  if Count = 0 then
  begin
    Exit(-1);
  end;
  for I := Index to Index + Count - 1 do begin
    if Comparer.Equals(Item, Values[I]) then begin
      Exit(I);
    end;
  end;
  Result := -1;
end;

class function TArrayExt.IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>): Integer;
begin
  Result := IndexOf<T>(Values, Item, Comparer, Low(Values), Length(Values));
end;

class function TArrayExt.IndexOf<T>(const Values: array of T; const Item: T): Integer;
begin
  result := IndexOf<T>(Values, Item, TEqualityComparer<T>.Default, Low(Values), Length(Values));
end;

一个简单的使用案例可能看起来像这样:
procedure Main;
var
  arr: TArray<Integer>;
  N: Integer;
begin
  arr := TArray<Integer>.Create(5, 7, 3, 4, 2);
  repeat
    Readln(N);
    N := TArrayExt.IndexOf(arr, N);
    Writeln(N);
  until false;
end;

1
TArray 是一个类,不能直接与动态数组兼容。这个解决方案并没有回答原始问题,即如何直接向动态数组类型添加方法。 - Remy Lebeau
1
@Remy,不,它并没有,但它实现了相同的功能。对我来说,“向动态数组类型添加方法”这个问题看起来像是一个XY问题,而且,这是一个好的和有效的答案,在我看来。与被接受的答案不同,它还具有适用于所有类型的数组的优点,而不仅仅是限于单个数组类型。 - Rudy Velthuis
@Rudy,但是当问题明确要求向类型添加实例方法时,我认为答案至少应该涉及到这一点。即使它随后提供了替代方法。毕竟这是一个问答网站。 - David Heffernan
@RemyLebeau,你能否提供一个示例,展示这个与动态数组不兼容的情况吗?我已经在10.1版本中尝试了TArray<Integer>array of IntegerTIntegerDynArray - Uwe Raabe
@David:好的,可以在前面加上一行代码解决这个问题。 <g> - Rudy Velthuis

1

对于 Delphi 2009 及以上版本,您可以使用通用列表:

uses System.Generics.Collections;

var
    MyArray : TList<integer>;
    i : integer;

begin
   MyArray := TList<integer>.Create;
   MyArray.Add(3);
   MyArray.Add(7);
   MyArray.Add(10);
   i := MyArray.IndexOf(10);
end;

在Delphi 2007中,您可以使用自定义记录:
type
   TMyArray = record
     private
      TheArray : array of integer;
     public
      procedure Add(Value : integer);
      function  IndexOf(Value : integer) : integer;
      function  Length : integer;
   end;   

procedure TMyArray.Add(Value : integer);

var
   i : integer;

begin
   i := length(TheArray);
   setlength(TheArray,i+1);
   TheArray[i] := Value; 
end;

function TMyArray.IndexOf(Value : integer) : integer;

var
   i : integer;

begin
   for i := 0 to length(TheArray)-1 do
      begin
         if TheArray[i] = Value then
            begin
               Result := i;
               exit; 
            end;
      end;
   Result := -1;
end;

function TMyArray.Length : integer;

begin
   Result := length(TheArray);
end;


procedure MyFunction;

  var
     MyArray : TMyArray;
     i : integer;

  begin
     MyArray.Add(3);
     MyArray.Add(7);
     MyArray.Add(10);
     i := MyArray.IndexOf(10);
  end;   

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接