有没有一种简单通用的方法可以将 scala 对象的代码包装在对象主体代码之前和之后执行的代码?



我正在尝试创建一些通用功能,允许我以通用方式添加要在 Scala 对象主体(使用 App 特征扩展(之前和之后执行的代码 - 类似于 Scalatest 的 BeforeAndAfterAll,但用于通用代码执行。

我很想找到一种通过特质来做到这一点的方法,但遇到了不足 - 比如

trait MyBeforeAndAfter {
def before = println("I am executed before!")
def after = println("I am executed after!")
}

object MyApp extends App with MyBeforeAndAfter {
println("I am generic code in the body that's wrapped") 
}

运行应用程序时的目标输出将是(关键功能是排序(:

I am executed before
I am generic code in the body that's wrapped
I am executed after

我考虑过但不知道该怎么做的两种方法是:

  1. 以某种方式将对象主体中的代码传递给负责包装的函数 - 但我不知道如何访问代码块作为我可以传递给函数的东西。我已经查看了App特征本身和Scalatest的BeforeAndAfterAll,以了解它们的方法。前者似乎在编译器中控制。后者,我处于初始的第五层,我还不清楚它是如何工作的。

  2. 将之前和之后拆分为单独的特征并将它们混合在一起,虽然特征排序非常简单,但我看不到任何方法可以注释特征应该在基本/目标对象/类之后执行。

堆栈溢出之神...帮助?

根据路易斯的问题进行编辑 - 基本上你为什么要这样做? 我最近与 akka 合作了很多构建,并且使用 akka 有很多 ActorSystem 设置/拆卸样板 - 从配置中抓取东西,建立系统等,我正在考虑清除一些样板的方法,以使主应用程序的独特部分更加可见。在其他一些上下文中,我也遇到了一些类似的愿望,我继续思考是否有一种通用的方法可以以"干净"的方式清除样板 - 这意味着类似于 App 特征(不需要覆盖,只需混合和去(,但也提供了在块之前和之后执行代码的灵活性。

标准方法是不在对象的主体中运行代码,而是在某个方法中运行代码

trait MyBeforeAndAfter {
def before() = println("I am executed before!")
def after() = println("I am executed after!")
def run(): Unit
def main(args: Array[String]): Unit = {
before()
run()
after()
}
}

object MyApp extends MyBeforeAndAfter {
override def run(): Unit = {
println("I am generic code in the body that's wrapped")
}
}

然后,如果您运行MyApp它将打印

I am executed before!
I am generic code in the body that's wrapped
I am executed after!

如果你真的想在对象的主体中运行代码,你可以定义宏注释

import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
class beforeAndAfter extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro BeforeAndAfterMacro.impl
}
object BeforeAndAfterMacro {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" :: Nil =>
val parents1 = parents :+ tq"MyBeforeAndAfter"
q"""$mods object $tname extends { ..$earlydefns } with ..$parents1 { $self =>
def run(): Unit = {
..$body
}
}"""
case _ =>
c.abort(c.enclosingPosition, "annottee must be an object")
}
}
}
@beforeAndAfter
object MyApp {
println("I am generic code in the body that's wrapped")
}
//Warning:scalac: object MyApp extends scala.AnyRef with MyBeforeAndAfter {
//  def <init>() = {
//    super.<init>();
//    ()
//  };
//  def run(): Unit = println("I am generic code in the body that's wrapped")
//}

这里与 @DmytroMitin
的类似方法唯一的区别是,这允许您自定义和重用beforeforeach方法。

trait BeforeAndAfter {
def before(): Unit
def after(): Unit
}
trait Program { self: BeforeAndAfter =>
def run(args: List[String]): Unit

final def main(args: Array[String]): Unit = {
self.before()
run(args.toList)
self.after()
}
}

你可以像这样使用:

trait MyBeforeAndAfter extends BeforeAndAfter {
override final def before(): Unit = {
println("Setup")
}

override final def after(): Unit = {
println("Clean up")
}
}
object MyApp extends Program with MyBeforeAndAfter {
override final def run(args: List[String]): Unit = {
println("Program")
}
}

您可以在此处看到它正在运行。

您应该将beforeafter用作函数,然后在execute方法中调用它们。execute方法以自定义函数为输入,并按一定顺序执行。这是一种标准方法,无论您是在main还是在另一个类中调用它,它都有效。

trait BeforeAfter {
def execute(myfunction : () => Unit) = {
before()
myfunction()
after()
}
def before() = println("before")
def after() = println("after")
}
object MyApp extends App with BeforeAfter{
def myprinter() = println("printing main")
execute(myprinter)

}

结果:

before
printing main
after

最新更新