“使用/尝试资源”的简单 Scala 模式(自动资源管理)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25634455/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 06:32:09  来源:igfitidea点击:

Simple Scala pattern for "using/try-with-resources" (Automatic Resource Management)

scalaresource-management

提问by clay

C# has usingwith the IDisposableinterface. Java 7+ has identical functionality with tryand the AutoCloseableinterface. Scala lets you choose your own implementation to this issue.

C#有usingIDisposable接口。Java 7+ 具有与tryAutoCloseable接口相同的功能。Scala 允许您针对此问题选择自己的实现。

scala-arm seems to be the popular choice, and is maintained by one of the Typesafe employees. However, it seems very complicated for such a simple behavior. To clarify, the usage instructions are simple, but understanding how all that code is working internally is rather complex.

scala-arm 似乎是流行的选择,由 Typesafe 的一名员工维护。然而,对于这样一个简单的行为来说,似乎非常复杂。澄清一下,使用说明很简单,但了解所有代码在内部如何工作却相当复杂。

I just wrote the following super simple ARM solution:

我刚刚写了以下超级简单的ARM解决方案:

object SimpleARM {
  def apply[T, Q](c: T {def close(): Unit})(f: (T) => Q): Q = {
    try {
      f(c)
    } finally {
      c.close()
    }
  }
}
  • Is there any benefit to something like simple-arm? It seems all the extra complexity should deliver extra benefit.
  • Normally, it is highly preferable to use a public, open source, library that is supported by others for general purpose behavior over using custom code.
  • Can anyone recommend any improvements?
  • Are there any limitations to this simple approach?
  • 像 simple-arm 这样的东西有什么好处吗?似乎所有额外的复杂性都应该带来额外的好处。
  • 通常,与使用自定义代码相比,使用其他人支持的公共、开源库进行通用行为是非常可取的。
  • 任何人都可以推荐任何改进吗?
  • 这种简单的方法有什么限制吗?

采纳答案by clay

Here is my newer simple, understand at a glance, Scala ARM. This fully supports every use case I can think of including multiple resources and yield values. This uses a very simple for comprehension usage syntax:

下面是我较新的简单,一目了然,Scala ARM。这完全支持我能想到的每个用例,包括多个资源和产量值。这使用了一个非常简单的理解用法语法:

class AutoCloseableWrapper[A <: AutoCloseable](protected val c: A) {
  def map[B](f: (A) => B): B = {
    try {
      f(c)
    } finally {
      c.close()
    }
  }

  def foreach(f: (A) => Unit): Unit = map(f)

  // Not a proper flatMap.
  def flatMap[B](f: (A) => B): B = map(f)

  // Hack :)    
  def withFilter(f: (A) => Boolean) = this
}

object Arm {
  def apply[A <: AutoCloseable](c: A) = new AutoCloseableWrapper(c)
}

Here's demo use:

这是演示使用:

class DemoCloseable(val s: String) extends AutoCloseable {
  var closed = false
  println(s"DemoCloseable create ${s}")

  override def close(): Unit = {
    println(s"DemoCloseable close ${s} previously closed=${closed}")
    closed = true
  }
}

object DemoCloseable {
  def unapply(dc: DemoCloseable): Option[(String)] = Some(dc.s)
}

object Demo {
  def main(args: Array[String]): Unit = {
    for (v <- Arm(new DemoCloseable("abc"))) {
      println(s"Using closeable ${v.s}")
    }

    for (a <- Arm(new DemoCloseable("a123"));
         b <- Arm(new DemoCloseable("b123"));
         c <- Arm(new DemoCloseable("c123"))) {
      println(s"Using multiple resources for comprehension. a.s=${a.s}. b.s=${b.s}. c.s=${c.s}")
    }

    val yieldInt = for (v <- Arm(new DemoCloseable("abc"))) yield 123
    println(s"yieldInt = $yieldInt")

    val yieldString = for (DemoCloseable(s) <- Arm(new DemoCloseable("abc")); c <- s) yield c
    println(s"yieldString = $yieldString")

    println("done")
  }
}

回答by cchantep

Your approach with a single simple loan pattern is working fine as long as you don't need to work with several resources, all needing to be managed. That's allowed with scala-arm monadic approach.

只要您不需要使用多个资源,所有资源都需要进行管理,那么您使用单个简单贷款模式的方法就可以正常工作。Scala-arm monadic 方法允许这样做。

import resource.managed

managed(openResA).and(managed(openResB)) acquireFor { (a, b) => ??? }

val res = for {
  a <- managed(openResA)
  b <- managed(openResB)
  c <- managed(openResC)
} yield (a, b, c)

res acquireAndGet {
  case (a, b, c) => ???
}

Main functions to know in scala-arm is resource.managedand .acquired{For,AndGet}, not really complex btw.

Scala-arm 中要知道的主要功能是resource.managedand .acquired{For,AndGet},顺便说一句,并不是很复杂。

回答by david.perez

This is the code I use:

这是我使用的代码:

def use[A <: { def close(): Unit }, B](resource: A)(code: A ? B): B =
    try
        code(resource)
    finally
        resource.close()

Unlike Java try-with-resources, the resource doesn't need to implement AutoCloseable. Only a close()method is needed. It only supports one resource.

与 Java try-with-resources 不同,资源不需要实现AutoCloseable。只close()需要一种方法。它只支持一种资源。

Here is an example use with an InputStream:

这是一个使用 的示例InputStream

val path = Paths get "/etc/myfile"
use(Files.newInputStream(path)) { inputStream ?
    val firstByte = inputStream.read()
    ....
}

回答by Peter Ertl

this one works for me really well:

这个对我来说非常有效:

  implicit class ManagedCloseable[C <: AutoCloseable](resource: C) {
    def apply[T](block: (C) => T): T = {
    try {
      block(resource)
    } finally {
      resource.close()
    }
  }

using it for example in this Apache Cassandra client code:

例如在这个 Apache Cassandra 客户端代码中使用它:

val metadata = Cluster.builder().addContactPoint("vader").withPort(1234).build() { cluster =>
  cluster.getMetadata
}

or even shorter:

甚至更短:

val metadata = Cluster.builder().addContactPoint("sedev01").withPort(9999).build()(_.getMetadata)

回答by Arioch 'The

http://illegalexception.schlichtherle.de/2012/07/19/try-with-resources-for-scala/

http://illegalexception.schlichtherle.de/2012/07/19/try-with-resources-for-scala/

Another implementation, probably more clean from "follow Java specifications" viewpoint, but also fails to support multiple resources

另一种实现,从“遵循 Java 规范”的角度来看可能更干净,但也无法支持多种资源

回答by Johnny

An improvement I can recommend to the approach you suggested, which is:

我可以对您建议的方法进行改进,即:

  def autoClose[A <: AutoCloseable, B](resource: A)(code: A ? B): B = {
    try
      code(resource)
    finally
      resource.close()
  }

Is to use:

是使用:

  def autoClose[A <: AutoCloseable, B](resource: A)(code: A ? B): Try[B] = {
    val tryResult = Try {code(resource)}
    resource.close()
    tryResult
  }

IMHO having the tryResult which is an Try[B], will allow you an easier control flow later.

恕我直言, tryResult 是一个Try[B], 将使您以后更容易控制流程。

回答by ChoppyTheLumberHyman

Choppy's Lazy TryClose monad might be what you are looking for (disclosure: I'm the author). It is very similar to Scala's Try but automatically closes resources automatically.

Choppy 的 Lazy TryClose monad 可能是您正在寻找的(披露:我是作者)。它与 Scala 的 Try 非常相似,但会自动关闭资源。

val ds = new JdbcDataSource()
val output = for {
  conn  <- TryClose(ds.getConnection())
  ps    <- TryClose(conn.prepareStatement("select * from MyTable"))
  rs    <- TryClose.wrap(ps.executeQuery())
} yield wrap(extractResult(rs))

// Note that Nothing will actually be done until 'resolve' is called
output.resolve match {
    case Success(result) => // Do something
    case Failure(e) =>      // Handle Stuff
}

See here for more info: https://github.com/choppythelumberHyman/tryclose

请参阅此处了解更多信息:https: //github.com/choppythelumberHyman/tryclose