是否可以编写一个将任何序数类型作为参数的方法?和 Inc() 或 High() 一样吗?
我正在使用德尔福 2007
是否可以编写一个将任何序数类型作为参数的方法?和 Inc() 或 High() 一样吗?
我正在使用德尔福 2007
您需要使用无类型参数:
procedure Foo(const ordinal);
或者
procedure Foo(var ordinal);
当然,由于您放弃了类型系统,因此您在这样的例程中可以做的事情有些受限。
找到了一个可能的方法,可能不是你所期望的,但是,嘿,我找到了一个方法!使用Variants
. 将无类型参数传递给过程的问题是你得到一个普通的指针,没有类型信息,所以你不能用它做任何有用的事情。字节为 1 个字节,最多 256 个元素的枚举为 1 个字节,最多 2^16 个元素的枚举为 2 个字节,整数为 4 个字节(除非它们是 8 个)。但是有一种类型允许传递任何东西并关心足够的类型信息以使事情正常工作:Variant
. 我特意在 Delphi 7 中编写了以下示例,以确保我不会意外使用任何 Delphi 2010 或 Delphi XE 优点。
编辑:更新了代码示例以处理任何被Variants.VarTypeIsOrdinal
. 这包括所有整数类型 + 布尔值。显然 Enum 被视为字节,所以它也吞下了它。
program Project1;
{$APPTYPE CONSOLE}
uses
ExceptionLog,
SysUtils, Variants;
type TSomeEnum = (e0, e1, e2, e3, e4);
procedure DoSomethingWithEnum(V: Variant);
var i: Integer;
b: Byte;
lw: LongWord; // Cardinal!
i64: Integer;
begin
case VarType(V) of
varInt64:
begin
i64 := V;
WriteLn(i64);
end;
varSmallint, varInteger, varShortInt:
begin
i := V;
WriteLn(i);
end;
varByte:
begin
b := V;
WriteLn(b);
end;
varWord, varLongWord:
begin
lw := V;
WriteLn(lw);
end;
varBoolean:
begin
if V then WriteLn('True') else WriteLn('False');
end;
else WriteLn('NOT a variant type (type = #' + IntToStr(Ord(VarType(V))));
end;
end;
var i: Integer;
b: Byte;
c: Cardinal;
enum: TSomeEnum;
w: Word;
si: Shortint;
begin
i := 1;
b := 2;
c := 3;
enum := e4;
w := 5;
si := -6;
DoSomethingWithEnum(i);
DoSomethingWithEnum(b);
DoSomethingWithEnum(c);
DoSomethingWithEnum(enum);
DoSomethingWithEnum(True);
DoSomethingWithEnum(w);
DoSomethingWithEnum(si);
Readln;
end.
这样做很困难的原因是 Inc(x)、Dec(x) 和其他像 Pred(x) 和 Succ(x) 实际上是由编译器生成的,如果你愿意,它们只是 Function 样式语法糖通过固有的编译器操作。
正如一些人建议的那样,你可以通过重载来做一些事情,其中一些可以巧妙地使用泛型,还有一些可以使用变体。但是没有什么比模拟这些功能更方便了,或者在功能上完全一样。
例如,编译器为所有有序类型实现 Inc(),包括枚举、整数和这些类型的子范围(经典“Wirth”Pascal 的一个现在相当模糊的特性是所有类型都可以在这些类型上定义子范围)。
如果你真的告诉我们更多关于你在做什么,它可能会更接近。但一般的答案是,不,甚至没有 Inc 和 Dec 的源代码,因为它们是编译器原语。如果函数 Inc 有 RTL 源代码,您可以查看并修改它。
inc(x) 可以定义为 x := Succ(x),但是,你如何定义 Succ(x)?作为 x := Inc(x)?你看。在某些时候,编译器“魔术”接管了。