【问题标题】:From VBA to Delphi conversion (Optional arguments issue)从 VBA 到 Delphi 转换(可选参数问题)
【发布时间】:2017-02-03 15:24:39
【问题描述】:

目前,我正在将一个用 VBA 编写的项目转换为 Delphi,并偶然发现了在转换一些带有 Optional 参数的 Subs 时出现的问题。 比如说,有一个 Sub 声明(只是一个例子,实际的 Subs 最多有 10 个可选参数):

Sub SetMark
    (x0 As Double, y0 As Double, 
     Optional TextOffset As Integer =5,
     Optional TextBefore As String = "",
     Optional Text As String = "",
     Optional TextAfter As String = "mm",
     Optional Color As String = "FFFFFF",
     Optional ArrowPresent As Boolean = True)

Sub 随后可以这样调用:

    Call SetMark (15, 100,,,"135")
    Call SetMark (100, 100, 8,, "My text here..", "")
    'a lot of calls here

Optional 参数在这里非常灵活,您可以省略其中任何一个,也可以为其中任何一个赋值。与 Delphi 不同。

Procedure SetMark
    (x0: real; y0: real, 
            TextOffset: Integer =5;
     TextBefore: ShortString = '';
     Text: ShortString = '';
     TextAfter: ShortString = 'mm';
     Color: ShortString = 'FFFFFF';
     ArrowPresent: Boolean = True);

看来您不能只复制 VBA 调用:

SetMark (15, 100,,,'135');// error here 

所以,问题是:有什么方法可以将 Subs 转换为 Delphi 程序,保持参数的相同灵活性? 我的第一个想法是使用默认参数,但它不起作用。 就目前而言,在 Delphi 中,我将不得不直接传递列表中的所有参数及其值,但这意味着需要大量工作来审查和正确移植 VBA 调用。

有什么想法吗?

【问题讨论】:

  • 顺便说一句,你真的不应该使用ShortString。这已经被弃用了很多年。使用原生字符串类型string
  • 另外,切线地,real 是一个遗留类型,它只是为了兼容性而真正存在(它是double 的别名)。最好在 Delphi 代码中声明一个double,就像 VB 代码一样。

标签: excel delphi vba


【解决方案1】:

有什么方法可以将 VBA 子程序转换为 Delphi 程序,同时在参数上保持同样的灵活性?

没有办法实现这一点——除了列表末尾之外,省略参数的灵活性根本不存在。

对于自动化对象的方法,您可以使用命名参数,如下所述:Named/optional parameters in Delphi? 但是,我非常建议您不要为了获得该功能而将类实现为自动化对象。

每当您在语言之间切换时,都会发现不方便的差异。这是不可避免的。最好的方法是尝试在新语言中找到解决问题的最佳方法,而不是试图将成语从旧语言强制转换为新语言。

在这种情况下,您可能希望使用 overloaded functionsparameter objects 来缓解这种不便。

【讨论】:

  • 是的。重载将是我的首选解决方案。
  • 能否请您指定如何实现重载,例如在调用指定参数TextTextAfter 之间进行区分。
  • @J... 构造函数很特别。当您调用它时,将实例化一个新对象,然后对其进行初始化。另一方面,记录构造函数只是初始化。它不实例化。我习惯于看到像obj := TMyClass.Create 这样的代码,并且知道我需要将它与对obj.Free 的调用配对。记录并非如此。我发现使用rec := TMyRec.Create 会引起混淆。 obj 是值类型还是引用类型?通过保留使用构造函数来引用类型,我可以确信我知道我在处理什么。
  • @J... 另一个问题是记录上不允许使用无参数构造函数。如果您是FreeAndNiler,那么您可能会因不小心将您的记录传递给FreeAndNil 而被抓住。实际上,这确实是停止使用FreeAndNil 的原因,不是吗?最后,我不喜欢我可以在实例上调用构造函数。我可以写rec.Create(666);。我宁愿不允许那样。我的约定是一个名为New 的静态类方法。所以我会初始化这样的记录:rec := TMyRec.New。通过遵循该约定,我发现更容易确定自己知道自己在处理什么。
  • @J... 现在在这种情况下,提问者使用的是 Delphi 7,既不能使用构造函数,也不能使用静态类函数。裸机功能是他唯一的选择。
【解决方案2】:

只是为了扩展重构以使用参数对象的想法,您可以声明如下记录:

 TSetMarkParams = record
   x0 : double; 
   y0 : double; 
   TextOffset : integer;
   TextBefore : string;
   Text : string;
   TextAfter : string;
   Color : string;
   ArrowPresent : boolean;
   constructor Create(Ax0, Ay0 : double);
 end;

并实现构造函数以填充默认值:

 constructor TSetMarkParams.Create(Ax0, Ay0 : double);
 begin
   x0 := Ax0;
   y0 := Ay0;
   TextOffset := 5;
   TextBefore := '';
   Text := '';
   TextAfter := 'mm';
   Color := 'FFFFFF';
   AllowPresent := true;
 end;

你的程序会有签名:

 procedure SetMark(ASetMarkParams : TSetMarkParams);

然后,您可以使用 SetMark (15, 100,,,'135'); 调用示例:

 var 
   LSetMarkParams : TSetMarkParams
 begin
   LSetMarkParams := TSetMarkParams.Create(15, 100);
   LSetMarkParams.Text := '135';
   SetMark(LSetMarkParams);
 end;

作为附带的好处,上面的内容更具可读性,因为它可以让您在返回调试麻烦的方法调用时避免盲目尝试计算逗号。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-01
    • 1970-01-01
    相关资源
    最近更新 更多