是否有一种直接而通用的方法来将 Scala 对象的代码与要在对象主体代码之前和之后执行的代码包装起来?

Is there a straightforward and general way to wrap a scala object's code with code to be executed before and after the object body code?

我正在尝试创建一些通用功能,允许我以通用方式添加要在 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. 将之前和之后分成单独的特征并将它们混合在一起,虽然特征排序非常简单,但我看不出有任何方法可以注释特征应该在基础 /目标对象 / class.

Stack Overflow 之神...帮忙?

根据 Luis 的问题进行编辑 - 基本上您为什么要这样做? 我最近使用 akka 进行了大量构建工作,使用 akka 有很多 ActorSystem 设置/拆卸样板文件——从配置中抓取东西,建立系统等,我正在想办法清除一些样板文件以制作主应用程序的独特部分更加明显。我 运行 在其他一些情况下也有类似的需求,我继续思考是否有一种通用的方法可以以“干净”的方式清除样板 - 类似于 App 特征(不需要覆盖, just mixin and go), 而且还可以灵活地在块之前和之后执行代码。

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

中调用它,它都有效。
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

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

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")
  }
}

可以看到运行here.