【问题标题】:evaluate string as code VBA将字符串评估为代码 VBA
【发布时间】:2017-05-26 05:37:50
【问题描述】:

我有一个带有可选参数的子程序

Public Sub FaR_Wild_Stories_Extras(ByVal rngStory As Word.Range, _
    ByVal strSearch As String, ByVal strReplace As String, _
    Optional extra1 As String = "", Optional extra2 As String = "")
  With rngStory.Find
    .ClearFormatting
    .Replacement.ClearFormatting
    .Text = strSearch
    .Replacement.Text = strReplace
    .Forward = True
    .MatchWildcards = True
    .MatchCase = True
    .IgnorePunct = True
    .IgnoreSpace = True
    .Format = False
    .Wrap = wdFindContinue
    If extra1 <> "" Then eval(extra1)
    If extra2 <> "" Then eval(extra2)
    .Execute Replace:=wdReplaceAll
  End With
End Sub

我希望能够将诸如“.font.size=14”或.MatchDiacritics = False 之类的内容放在extra1 中并对其进行评估,以便在调用查找和替换子例程时,如果有特定的额外参数我想要一次性包含在查找和替换中,我不必为它创建一个完整的单独子。

有没有办法做到这一点? Eval() 似乎在 word VBA 中不存在。 Object.string 是不可能的,除了复制子例程或拥有一个包含 10 个很少使用的可选参数的子例程之外,还有什么更聪明的方法可以构建我的代码吗?

对于更笼统的情况,是否可以像这样call一个sub,或者通过巧妙地传递参数来构造代码以达到相同的效果

...
call Example , true, ".prop1=5", ".prop2=6"
...

Sub Example1(x, Optional param1 As String, Optional param2 As String)
    With Application.ExampleObject
        .property1 = "foo"
        .property2 = x
        Expand (param1)
        Expand (param2)
        .excecute
    End With
End Sub

【问题讨论】:

  • 我认为应该是Evaluate(),但我完全不确定这是否可行
  • evaluate() 是一个特定于 Excel 的函数,适用于工作表公式而不是 vba 代码(例如 evaluate("sum(a1:d4))"
  • 即使在 excel 中,eval/Evaluate 也只是一种调用函数的机制。您在示例中尝试做的是将属性设置为对象。据我所知,VBA 不支持这样的功能。要调整属性,您首先需要一个对象实例,然后反映它以替换/添加属性值。 .Net/Java 支持反射,VBA 不支持。
  • 那么你建议我做什么?我可以为最终使用的每个属性组合创建一个新的子(涉及大量代码重复)?我可以为我将来可能会使用的每个属性创建一个带有一堆可选参数的 sub 的庞然大物(更好,但仍然丑陋)或者是否有更聪明的方法可以实现类似的目的,也就是说调用包含由多个参数定义的方法的子例程,而没有在子例程的参数中指定所有这些可能使用的参数?
  • @cyboashu 事实证明这实际上是可能的,请参阅下面的答案。

标签: vba replace eval ms-word execute


【解决方案1】:

聚会有点晚了,但在这种情况下,我会使用stdLambda

stdLambda 是一个外部库,(主要由我自己开发)为 VBA 带来完整的解析器、编译器和评估器。我们解析类似 VBA 的代码,将其转换为字节码,然后在需要时对其进行评估,所有这些都在 VBA 运行时内完成!

这个用例的用法应该非常简单:

Public Sub FaR_Wild_Stories_Extras(ByVal rngStory As Word.Range, _
ByVal strSearch As String, ByVal strReplace As String, _
Optional ByVal extraFilters As stdICallable)

    Dim f as Find
    set f = rngStory.Find
    With f
       'Default search params:
       .ClearFormatting
       .Replacement.ClearFormatting
       .Text = strSearch
       .Replacement.Text = strReplace
       .Forward = True
       .MatchWildcards = True
       .MatchCase = True
       .IgnorePunct = True
       .IgnoreSpace = True
       .Format = False
       .Wrap = wdFindContinue
       Call extraFilters.Run(f)
       .Execute Replace:= wdReplaceAll
    End With
 End Sub
 Sub Main()
    Call FaR_Wild_Stories_Extras(Doc.Contents, "Hello","Bye", stdLambda.CreateMultiline( _ 
        "$1.Font.Size = 10", _ 
        "$1.MatchDiacritics = False" _ 
    ))
 End Sub

【讨论】:

    【解决方案2】:

    最佳选择

    CallByName 允许您使用由字符串命名的对象设置属性或调用方法。

    Sub Example(x, Optional Method1 As String ="", _
                Optional Param1 As String = "", Optional Param1Value as Variant)
    
        Dim MyObject as Object 
        Set MyObject = Application.ExampleObject
    
        'invoke a method from its name
        if method1 <> "" Then CallByName MyObject, Method1, VbMethod
        'set a property from its name and a variant representing the value to set
        if param1 <> "" Then CallByName MyObject, Param1, VbSet, Param1Value
    
        With MyObject
            .property1 = "foo"
            .property2 = x       
            .excecute
        End With
    End Sub
    

    (有关如何优雅地处理具有多个值的属性的信息,请参阅此线程http://www.vbforums.com/showthread.php?405366-RESOLVED-Using-CallByName-with-variable-number-of-arguments

    早期的解决方案

    将很少使用的属性分配给外部子程序。如果您不想弄乱主子例程的参数,则与上述组合仍然很有用。

    Sub Example(x, Optional ExternalSub as String)
        'pull any additional rarely used properties from another sub
        Application.Run(ExternalSub)
    
        With Application.ExampleObject
            .property1 = "foo"
            .property2 = x       
            .excecute
        
    End Sub
    
    Sub External_MoreProperties
        Application.ExampleObject.property3 = "bar"
    End Sub
    

    【讨论】:

    • 很好的答案,但也许“无法将字符串评估为 vba 中的代码”应该阅读“没有直接的方法可以将字符串评估为 vba 中的代码”。 VBA 是图灵完备的。如果情况变得更糟,您可以编写自己的解析器。此外——VBA 编辑器是可编写脚本的,您可以使用它来动态生成代码。但是,没有什么比例如Python 的exec() 函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-17
    • 1970-01-01
    • 1970-01-01
    • 2019-01-06
    • 2022-01-19
    • 1970-01-01
    相关资源
    最近更新 更多