操作符重载是语法上的一些基础用法,但之前我不太了解。最近看了下文档,写了一下测试代码,记录在这里。
首先看这个链接:
这个链接文章的作者是 Marco Cantu,一个资深的 Delphi 专家。他在这篇文章里面的代码例子:
- type
- TNumber = class
- private
- FValue: Integer;
- procedure SetValue(const Value: Integer);
- public
- property Value: Integer read FValue write SetValue;
- class operator Add (a, b: TNumber): TNumber;
- class operator Implicit (n: TNumber): Integer;
- end;
-
- //---------- 上述代码的用法 ----------
-
- a, b, c: TNumber;
- ...
- c := a + b;
- ShowMessage (IntToStr (c));
解释一下:对于 TNumber 这个东西(一个 class,一个类),这个类型的变量,可以直接用加法符号,直接相加。就是因为它有 class operator add(a, b: TNumber) 这样的代码在里面。
问题是,这段代码在我的 Delphi 10.4 CE 版上编译无法通过,IDE 提示语法错误。
我把 TNumber = class 改为 TNumber = record 结果可以编译通过。
这里的 class operator 就是对加法这个操作符,做了一个【重载】,针对这个话题,Delphi 官方的文档链接在下面:
Operator Overloading (Delphi) - RAD Studio (embarcadero.com)
上述链接的文章的标题是:Operator Overloading
以下代码在 Delphi 10.4 CE 版上测试通过。
- type
- TMyEvent = procedure(const i: Integer) of object;
-
- TForm1 = class(TForm)
- Button1: TButton;
- Label1: TLabel;
- Memo1: TMemo;
- Button2: TButton;
- procedure Button1Click(Sender: TObject);
- procedure Button2Click(Sender: TObject);
- private
- { Private declarations }
- procedure DoOnEvent(const i: Integer);
- procedure DoOnEvent2(const i: Integer);
- public
- { Public declarations }
- end;
-
- // class operator 在 record 里面可用。
- TNumber = record
- private
- FValue: Integer;
- FValue2: Integer;
- FOnEvent: TMyEvent;
-
- procedure SetValue(const Value: Integer);
- public
- property Value: Integer read FValue write SetValue;
- property MyEvent: TMyEvent read FOnEvent write FOnEvent;
-
- class operator Add(a, b: TNumber): TNumber;
- class operator Implicit(n: TNumber): Integer; //Implicit 这个单词:隐含的,这里的定义,是指在代码里面可以直接拿 TNumber 当作一个 Integer 用。
- class operator Explicit(i: Integer): TNumber;
- end;
-
-
- var
- Form1: TForm1;
-
- implementation
-
- {$R *.dfm}
-
- { TNumber }
-
- class operator TNumber.Add(a, b: TNumber): TNumber;
- begin
- Result.FValue := a.FValue + b.FValue;
- end;
-
- class operator TNumber.Explicit(i: Integer): TNumber;
- begin
- Result.FValue := i;
- end;
-
- class operator TNumber.Implicit(n: TNumber): Integer;
- begin
- Result := n.FValue;
- end;
-
- procedure TNumber.SetValue(const Value: Integer);
- begin
- Self.FValue := Value;
- Self.FOnEvent(Value);
- end;
-
- procedure TForm1.Button1Click(Sender: TObject);
- var
- a, b, c: TNumber;
- begin
- a.MyEvent := Self.DoOnEvent;
- b.MyEvent := Self.DoOnEvent2;
- c.MyEvent := Self.DoOnEvent;
-
- a.Value := 2;
- b.Value := 5;
- c := a + b;
-
- Label1.Caption := c.Value.ToString;
-
- Memo1.Lines.Add(IntToStr(C)); // class operator Implicit(n: TNumber): Integer; 这句话的作用,就是 C: TNumber 可以直接当 Integer 用。
- end;
-
- procedure TForm1.Button2Click(Sender: TObject);
- var
- a: TNumber;
- i: Integer;
- begin
- i := 3;
- a := TNumber(i); //class operator Explicit(i: Integer): TNumber; 这句让 a := TNumber(i) 起作用。但不能直接 a := i;
-
- end;
-
- procedure TForm1.DoOnEvent(const i: Integer);
- begin
- Memo1.Lines.Add('Event1: ' + i.ToString);
- end;
-
- procedure TForm1.DoOnEvent2(const i: Integer);
- begin
- Memo1.Lines.Add('Event2: ' + i.ToString);
- end;
1. 对于一个 Record 结构体类型来说,可以通过操作符重载的方式,把加号操作符,重新定义。也就是 TNumber 这个结构体的这部分代码:
- TNumber = record
- FValue: Integer;
- class operator Add(a, b: TNumber): TNumber; //重新定义了加法
-
-
- //重新定义的加法的实现
- class operator TNumber.Add(a, b: TNumber): TNumber;
- begin
- Result.FValue := a.FValue + b.FValue;
- end;
有了上述重载加法符号,则下面的代码就可以正常执行:
- procedure TForm1.Button1Click(Sender: TObject);
- var
- a, b, c: TNumber;
- begin
- a.Value := 2;
- b.Value := 5;
- c := a + b;
-
- Label1.Caption := c.Value.ToString;
- end;
上述代码里面,a, b, c 三个 TNmber 类型的结构体变量,可以直接用加法符号来相加!
2. 操作符里面还有两个玩意:Implicit 和 Explicit,关于它的代码如下:
- TNumber = record
- private
- FValue: Integer;
- public
- class operator Implicit(n: TNumber): Integer;
- class operator Explicit(i: Integer): TNumber;
- end;
-
-
- //实现代码如下
- class operator TNumber.Explicit(i: Integer): TNumber;
- begin
- Result.FValue := i;
- end;
-
- class operator TNumber.Implicit(n: TNumber): Integer;
- begin
- Result := n.FValue;
- end;
这两个 class operator 究竟是什么意思呢?用翻译软件翻译这个英文单词,也不知道它究竟是什么意思,干嘛用的。用测试代码看看它能干什么:
- procedure TForm1.Button1Click(Sender: TObject);
- var
- a, b, c: TNumber;
- begin
- a.MyEvent := Self.DoOnEvent;
- b.MyEvent := Self.DoOnEvent2;
- c.MyEvent := Self.DoOnEvent;
-
- a.Value := 2;
- b.Value := 5;
- c := a + b;
-
- Label1.Caption := c.Value.ToString;
-
- // class operator Implicit(n: TNumber): Integer; 这句话的作用,就是 C: TNumber 可以直接当 Integer 用。
- Memo1.Lines.Add(IntToStr(C));
- end;
对 Implicit 这个操作符的重载代码,让程序可以把这个 TNumber 结构体的变量,直接当作整数来用,把它放到 IntToStr() 函数里面,编译器不会报错,运行得到正确的结果!
继续看另外一个玩意的测试代码:
- procedure TForm1.Button2Click(Sender: TObject);
- var
- a: TNumber;
- i: Integer;
- begin
- i := 3;
-
- //class operator Explicit(i: Integer): TNumber;
- //这句让 a := TNumber(i) 起作用。但不能直接 a := i;
- a := TNumber(i);
- Memo1.Lines.Add(IntToStr(a));
- end;
上述代码中,可以直接通过类型转换,把一个整数 i 变成一个 TNumber 类型的结构体!编译不会出错,执行结果正确。这个功能的实现就依靠了前面的对 Explicit 这个操作符的重载代码。
一个类,可以在类定义里面,定义和事件有关的代码。然后就可以让这个类的实例对象,在需要触发某个事件的时候,触发一个事件的代码。前面我贴的完整测试代码里面,在一个 Record 结构体定义中,我也定义了一个事件变量,并且在使用一个 Record 变量实例中,给事件赋值了,测试到事件代码被执行了。
首先,我定义了一个用来做事件的方法类型:
- type
- TMyEvent = procedure(const i: Integer) of object;
假设你要的事件的方法类型,Delphi 自己本身就带了,那直接用,也不用自己定义了。比如 Delphi 自己有的一个类型,是使用 Delphi 的时候最常见的类型:
TNotifyEvent = procedure(Sender: TObject) of object;
平常见到的 OnClick 啊什么的,就是这个 TNotifyEvent 类型。
然后我在这个 Record 结构体里面,定义了一个事件变量:
- TNumber = record
- private
- FValue: Integer;
- FOnEvent: TMyEvent; //事件的变量
-
- procedure SetValue(const Value: Integer);
- public
- property MyEvent: TMyEvent read FOnEvent write FOnEvent; //事件
-
- property Value: Integer read FValue write SetValue; //属性
- end;
-
-
- //给它的属性 Value 设置属性时,触发事件。
-
- procedure TNumber.SetValue(const Value: Integer);
- begin
- Self.FValue := Value;
- Self.FOnEvent(Value);
- end;
这里,给一个 Record 结构体,可以有 private 和 public 关键词,也可以有 property 关键词,简直和一个类一样了。
这里的 property MyEvent: TMyEvent 就是一个事件!
使用这个事件的代码如下:
- procedure TForm1.Button1Click(Sender: TObject);
- var
- a, b, c: TNumber;
- begin
- //结构体变量,可以有事件!
- a.MyEvent := Self.DoOnEvent; //为变量 a 的事件赋值方法;
- b.MyEvent := Self.DoOnEvent2; //为变量 b 的事件赋值;
- c.MyEvent := Self.DoOnEvent; //为变量 c 的事件赋值;
-
- a.Value := 2;
- b.Value := 5;
- c := a + b;
-
- Label1.Caption := c.Value.ToString;
- end;
-
-
- //事件方法如下:
- procedure TForm1.DoOnEvent(const i: Integer);
- begin
- Memo1.Lines.Add('Event1: ' + i.ToString);
- end;
-
- procedure TForm1.DoOnEvent2(const i: Integer);
- begin
- Memo1.Lines.Add('Event2: ' + i.ToString);
- end;
上述代码的执行结果是在 Memo1 里面看到两个不同的方法被执行了。
可以给 Record 结构体类型,重载操作符,比如加号,就是 add,重新用代码定义。还有很多其它操作符,也可以做重载,看本文前面的官方文档链接。从我自己写的这个测试代码来看,它可以让我们在使用 Record 结构体的时候,可以直接写 c := a + b; 这样的代码,如果没有重载 add 这个操作符号,可能就要这样写:
- var
- a, b, c: TNumber;
- begin
- a.Value := 3;
- b.Value := 5;
- c.Value := a.Value + b.Value;
-
- //如果没有给 TNumber 重载 add 操作符,就不能写以下代码:
- c := a + b;
- end;
那么,看起来,这里的作用就是让以后使用 TNumber 这个结构体的时候,简单一点,少写一些代码。还有没有其它的作用?需要继续尝试才知道。或者有文档资料介绍。
Record 类型,可以和 class 类型一样,可以定义属性和事件!
当然,Record 结构体类型,也可以有方法和函数。
那么,一个 Record 结构,和一个类,有什么区别呢?需要思考。