- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
这是一个假设场景。
我有很多用户名(比如 10,000,000,000,000,000,000,000。是的,我们正处于星际时代 :))。每个用户都有自己的数据库。我需要遍历用户列表并对每个数据库执行一些 SQL 并打印结果。
因为我学到了函数式编程的优点,并且因为我要与如此多的用户打交道,所以我决定使用 F# 和纯序列(又名 IEnumerable)来实现它。我走了。
// gets the list of user names
let users() : seq<string> = ...
// maps user name to the SqlConnection
let mapUsersToConnections (users: seq<string>) : seq<SqlConnection> = ...
// executes some sql against the given connection and returns some result
let mapConnectionToResult (conn) : seq<string> = ...
// print the result
let print (result) : unit = ...
// and here is the main program
users()
|> mapUsersToConnections
|> Seq.map mapConnectionToResult
|> Seq.iter print
漂亮吗?优雅的?绝对。
但是! 谁以及在什么时候处理 SqlConnections?
而且我不认为 mapConnectionToResult
应该做的答案 是正确的,因为它对给定的连接的生命周期一无所知。根据 mapUsersToConnections
的实现方式和各种其他因素,事情可能会起作用或不起作用。
由于 mapUsersToConnections
是唯一可以访问连接的其他地方,因此它必须负责处理 SQL 连接。
在 F# 中,可以这样做:
// implementation where we return the same connection for each user
let mapUsersToConnections (users) : seq<SqlConnection> = seq {
use conn = new SqlConnection()
for u in users do
yield conn
}
// implementation where we return new connection for each user
let mapUsersToConnections (users) : seq<SqlConnection> = seq {
for u in users do
use conn = new SqlConnection()
yield conn
}
C# 等价物是:
// C# -- same connection for all users
IEnumerable<SqlConnection> mapUsersToConnections(IEnumerable<string> users)
{
using (var conn = new SqlConnection())
foreach (var u in users)
{
yield return conn;
}
}
// C# -- new connection for each users
IEnumerable<SqlConnection> mapUsersToConnections(IEnumerable<string> user)
{
foreach (var u in users)
using (var conn = new SqlConnection())
{
yield return conn;
}
}
我执行的测试表明对象确实在正确的点被正确处理,即使并行执行的东西也是如此:一次在共享连接的整个迭代结束时;在非共享连接的每个迭代周期之后。
那么,问题:我做对了吗?
编辑:
有些回答友善地指出了代码中的一些错误,我也做了一些修正。编译的完整工作示例如下。
SqlConnection 的使用仅用于示例目的,它实际上是任何 IDisposable。
编译示例
open System
// Stand-in for SqlConnection
type SimpeDisposable() =
member this.getResults() = "Hello"
interface IDisposable with
member this.Dispose() = printfn "Disposing"
// Alias SqlConnection to our dummy
type SqlConnection = SimpeDisposable
// gets the list of user names
let users() : seq<string> = seq {
for i = 0 to 100 do yield i.ToString()
}
// maps user names to the SqlConnections
// this one uses one shared connection for each user
let mapUsersToConnections (users: seq<string>) : seq<SqlConnection> = seq {
use c = new SimpeDisposable()
for u in users do
yield c
}
// maps user names to the SqlConnections
// this one uses new connection per each user
let mapUsersToConnections2 (users: seq<string>) : seq<SqlConnection> = seq {
for u in users do
use c = new SimpeDisposable()
yield c
}
// executes some "sql" against the given connection and returns some result
let mapConnectionToResult (conn:SqlConnection) : string = conn.getResults()
// print the result
let print (result) : unit = printfn "%A" result
// and here is the main program - using shared connection
printfn "Using shared connection"
users()
|> mapUsersToConnections
|> Seq.map mapConnectionToResult
|> Seq.iter print
// and here is the main program - using individual connections
printfn "Using individual connection"
users()
|> mapUsersToConnections2
|> Seq.map mapConnectionToResult
|> Seq.iter print
结果是:
共享连接:“你好”“你好”...“处置”
个别连接:“你好”“处置”“你好”“处置”
最佳答案
我会避免这种方法,因为如果你的库的不知情用户做了类似的事情,结构就会失败
users()
|> Seq.map userToCxn
|> Seq.toList() //oops disposes connections
|> List.map .... // uses disposed cxns
. . ..
我不是这个问题的专家,但我认为最好的做法是不要让序列/IEnumerables 在它们产生它们之后弄乱它们,因为中间的 ToList() 调用会产生不同的结果而不是直接作用于序列——DoSomething(GetMyStuff()) 将不同于 DoSomething(GetMyStuff().ToList())。
实际上,为什么不对整个事情使用序列表达式,因为这样可以完全解决这个问题:
seq{ for user in users do
use cxn = userToCxn user
yield cxnToResult cxn }
(其中 userToCxn 和 cxnToResult 都是简单的一对一非处置函数)。这似乎比任何东西都更具可读性,应该会产生预期的结果,可并行化,并且适用于任何一次性用品。这可以使用以下技术转换为 C# LINQ:http://solutionizing.net/2009/07/23/using-idisposables-with-linq/
from user in users
from cxn in UserToCxn(user).Use()
select CxnToResult(cxn)
不过,另一种做法是先定义“getSomethingForAUserAndDisposeTheResource”函数,然后将其用作基本构建 block :
let getUserResult selector user =
use cxn = userToCxn user
selector cxn
一旦有了这个,就可以轻松地从那里开始构建:
//first create a selector
let addrSelector cxn = cxn.Address()
//then use it like this:
let user1Address1 = getUserResult addrSelector user1
//or more idiomatically:
let user1Address2 = user1 |> getUserResult addrSelector
//or just query dynamically!
let user1Address3 = user1 |> getUserResult (fun cxn -> cxn.Address())
//it can be used with Seq.map easily too.
let addresses1 = users |> Seq.map (getUserResult (fun cxn -> cxn.Address()))
let addresses2 = users |> Seq.map (getUserResult addrSelector)
//if you are tired of Seq.map everywhere, it's easy to create your own map function
let userCxnMap selector = Seq.map <| getUserResult selector
//use it like this:
let addresses3 = users |> userCxnMap (fun cxn -> cxn.Address())
let addresses4 = users |> userCxnMap addrSelector
如果您只需要一个用户,那么您就不必致力于检索整个序列。我想这里吸取的教训是让你的核心功能变得简单,这样可以更容易地在它之上构建抽象。请注意,如果您在中间某处执行 ToList,这些选项都不会失败。
关于c# - IEnumerable<IDisposable> : who disposes of what and when -- Did I get it right?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6778958/
我在 dispose 和 finalize 之间犹豫不决。这是我的示例代码: public class Car:IDisposable { public string name
我有一个 Activity,我在其中创建并订阅 Single 类的多个实例(每个实例都在单独的后台线程中执行一些工作)。对于每个订阅,我将创建的 Disposable 实例添加到 CompositeD
我有一个服务、一个存储库和一个 DbContext。存储库拥有 DbContext,服务拥有存储库。 我的存储库应该实现 IDisposable 吗?如果是这样,我的服务是否也应该实现 IDispos
我有一个基于字符串列表处理文件流的应用程序,字符串可以是磁盘上的文件,也可以是 Zip 文件中的文件。为了清理代码,我想重构打开文件的过程。 我创建了一个返回文件内容 Stream 的方法,但是因为流
有没有一种方法可以从 Dispose 方法中取消处置,换句话说,在调用 A.Dispose() 时不处置 A。 更新我没有很好地解释,因为我想不出一个很好的方式来表达它。我拥有的是一个在单独线程中运行
如以下示例实现即 https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose有一
我开始用 FXCop 来处理我的一些程序集,并注意到一些有趣的事情,我正试图全神贯注。 "Types that declare disposable members should also imple
当我创建表单时,自动生成的代码不包含重写的 Dispose 方法。这是否意味着没有为表单中的所有控件调用 Dispose? 最佳答案 当您在表单上调用 Dispose 时,它会为其 Controls
在我的 WPF 应用程序代码中,我收到以下警告: CA1001 Types that own disposable fields should be disposable Implement IDis
每当我读到 Close() 和 Dispose() 时,我都会看到很多只使用 Using Block 的引用资料,但我还没有找到如何在 WebMatrix C# Razor 语法中使用 Using B
我是 c# 和 VSTO 的新手。 我有一个 Excel 插件,我在其中创建了一个功能区和一些可以执行各种操作的新按钮。 我在 base.Dispose 中收到 NullReferenceExcept
我在 FCL 代码中发现了一个不寻常的样本。 这是 System.IO.BinaryReader 中的方法: protected virtual void Dispose(bool dispo
我正在尝试遵循以下教程并将其从 C# 转换为 Vb.net http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef
我有一个 LINQ 对象,其中添加了一个附加方法。该类没有一次性属性或方法,但 FxCop 引发错误“拥有一次性字段的类型应该是一次性的”并引用该类。 到目前为止我已经减少了代码,但仍然收到错误: p
因此,默认的处理模式实现如下所示: class SomeClass : IDisposable { // Flag: Has Dispose already been called? bo
我在 WinForms 应用程序中使用 RxFramework。我正在尝试运行 Observable 异步并使用 CancellationDisposable 在用户单击按钮时取消操作。但它不工作!
有很多从 .NET 中的函数返回 IDisposable 对象数组或列表的示例。例如,Process.GetProcesses()。 如果我调用该方法,我是否有责任在遍历数组的所有成员时对它们进行 D
如果我用 C# 编写一个实现 IDisposable 的类,为什么我不能简单地实现 public void Dispose(){ ... } 处理释放任何非托管资源? 是 protected virt
LibGDX - 是否应该先执行 texture.dispose() 然后再执行 batch.dispose()?还是没有什么不同? 我有以下简单的代码。我的问题是 texture.dispose()
当应用程序收到来自服务的调用时,它会为每个调用打开一个表单。用户必须在每个窗口中进行输入并关闭它。为了让用户的工作顺利进行,我试图在显示下一个窗口时重新激活用户正在处理的窗口。 执行此操作的方法如下:
我是一名优秀的程序员,十分优秀!