在Scala中获取运行时分配的变量的名称

时间:2014-10-13 07:19:35

标签: scala reflection

让一个班级

case class C(val i: Int) {
  var assignee: Option[String] = None
}

以及对名为x

的变量的给定赋值
val x = C(1)
x: C = C(1)

如何将值"x": String转换为x.assignee,即

assert( x.assignee == Some("x") )

1 个答案:

答案 0 :(得分:1)

运行时无法使用本地变量名称。但是您可以在编译时使用a macro捕获它。参见例如https://github.com/sbt/sbt/blob/9c442d3aed53bdc89db1ada9d5b204bf02adb339/main/settings/src/main/scala/sbt/std/KeyMacro.scala definingValName及其用法:

def definingValName(c: Context, invalidEnclosingTree: String => String): String = {
  import c.universe.{ Apply => ApplyTree, _ }
  val methodName = c.macroApplication.symbol.name
  def processName(n: Name): String = n.decoded.trim // trim is not strictly correct, but macros don't expose the API necessary
  def enclosingVal(trees: List[c.Tree]): String = {
    trees match {
      case vd @ ValDef(_, name, _, _) :: ts => processName(name)
      case (_: ApplyTree | _: Select | _: TypeApply) :: xs => enclosingVal(xs)
      // lazy val x: X = <methodName> has this form for some reason (only when the explicit type is present, though)
      case Block(_, _) :: DefDef(mods, name, _, _, _, _) :: xs if mods.hasFlag(Flag.LAZY) => processName(name)
      case _ =>
        c.error(c.enclosingPosition, invalidEnclosingTree(methodName.decoded))
        "<error>"
    }
  }
  enclosingVal(enclosingTrees(c).toList)
}

def enclosingTrees(c: Context): Seq[c.Tree] =
  c.asInstanceOf[reflect.macros.runtime.Context].callsiteTyper.context.enclosingContextChain.map(_.tree.asInstanceOf[c.Tree])