是否有不依赖 IDisposable 的使用模式?
我想创建一个内部消息系统,它可以告诉我调用某些代码的持续时间。 我考虑的是为了易用性,让 SystemMessage 类实现 IDisposable。
我会在 SystemMessage 的构造函数期间设置时间戳,如果调用 Dispose,我可以计算出持续时间。
问题是我不想对对象进行 GC 处理。 我希望它作为 MessageCollection 的一部分保留下来。
C# 中是否有另一种构造可以为我提供 using 语句的可用性,而无需踩踏 IDisposable 的预期功能。
Using (message = Collection.CreateNewMessage("FileDownlading"))
{
// I wonder how long it is taking me to download this file in production?
// Lets log it in a message and store for later pondering.
WebClass.DownloadAFile("You Know This File Is Great.XML");
}
// we fell out of the using statement, message will figure out how long
// it actually took to run.
// This was clean and easy to implement, but so wrong?
I am wanting to create an internal messaging system that can tell me the duration of some code being called. I was thinking for ease of use, to make the SystemMessage class implement IDisposable.
I would set a time stamp during the SystemMessage's constructor and if the Dispose was called, I could figure out the duration.
The problem is that I do not want to have the object GC'ed. I want it to stay around as part of a MessageCollection.
Is there another construct in C# that can give me the usability of the Using Statement without stepping on the intended function of IDisposable.
Using (message = Collection.CreateNewMessage("FileDownlading"))
{
// I wonder how long it is taking me to download this file in production?
// Lets log it in a message and store for later pondering.
WebClass.DownloadAFile("You Know This File Is Great.XML");
}
// we fell out of the using statement, message will figure out how long
// it actually took to run.
// This was clean and easy to implement, but so wrong?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
调用 Dispose 不会导致对象被 GC 处理 - 当 GC 进行扫描并且没有任何对象引用它时,就会发生这种情况。 如果您仍然通过 MessageCollection 引用该对象,它将保留下来。
Dispose 可以防止它被 Finalized,但由于您没有使用 Dispose 来清理资源,因此您不会有 Finalizer,也不会关心。
所以,真正唯一的问题是让你的类实现 IDisposable 的语义混乱,即使没有资源可以处理。
就我个人而言,我不认为这是一个问题。 如果消费者调用 Dispose,那么很好 - 他们会记录时间。 如果他们不这样做,那么他们就不会得到 itme 印章,最糟糕的情况是他们会违反 FxCop 规定。
然而,它有点不直观 - 所以如果这是供公共使用的,我建议提供一个更容易发现的替代方案,例如:
它将运行并计时一个 Action 委托。
Calling Dispose doesn't cause the object to be GC'ed - that happens when the GC does a sweep and nothing is referencing it. If you're still referencing the object via MessageCollection, it'll stick around.
Dispose can keep it from being Finalized, but since you're not using Dispose to clean up resources, you won't have a Finalizer and you won't care.
So, really the only problem is the confusing semantics around having your calss implement IDisposable even though there's no resources to dispose of.
Personally, I don't see it as a problem. If consumers call Dispose, then fine - they get the time logging. If they don't, then they don't get itme stamps and the worst that happens is they get an FxCop violation.
It is, however, a bit unintuitive - so if this is for public use, I'd suggest offering a more discoverable alternative like:
which would run and time an Action delegate instead.
您是否正在寻找类似于闭包的东西?
http://en.wikipedia.org/wiki/Closure_(computer_science)
您可以伪造一些东西...就像这样...
Are you looking for something akin to closures?
http://en.wikipedia.org/wiki/Closure_(computer_science)
You could fake something... like this...
我不认为使用是你想要的。 为什么不让构造函数记录时间,然后在调用 DownloadAFile 时记录时间增量? 在您的示例中,如果出现异常,它将记录异常的时间,就像当时下载文件一样。
如果您确实希望行为像您的示例一样,只需使用 try / finally 块,并在 finally 中进行日志记录。
using
只是 try/finally 块和调用Dispose
的语法糖。您的示例的等效代码如下所示:
I don't think using is what you want here. Why not just have the constructor record the time, and then when DownloadAFile is called log the time delta? In your example, if there's an exception, it will log the time of the exception as though the file was downloaded then.
If you actually want the behavior to be like your example, just use a try / finally block, and do the logging in the finally.
using
is just syntactic sugar for a try / finally block and a call toDispose
.The equivalent code to your example looks like this:
您可以在 DownloadAFile() 中使用 System.Diagnostics.Stopwatch 在每次调用时进行计时。
或者,只需在对 DownloadAFile() 的调用周围添加秒表代码(取决于您希望其如何工作)。
在这种情况下使用 IDisposable 并不是一个好主意。
You can use a System.Diagnostics.Stopwatch inside DownloadAFile() to do the timing every time it's called.
Or, just add the stopwatch code around the call to DownloadAFile() (depending on how you want this to work).
Using IDisposable in this case would not be a good idea.
我最近一直在关注这个问题,也许 PostSharp 可以帮助你。 它允许您使用一个属性来装饰一个方法,该属性将在您的方法启动和停止时被调用。
http://www.postsharp.org/
我不确定它会做你喜欢的事情,但是它值得研究,而且它有你渴望的“语法糖”!
克里斯
I have been looking at this lately and perhaps PostSharp can help you. It lets you decorate a method with an attribute that will be called when your method starts and when it stops.
http://www.postsharp.org/
I'm not sure it will do what you like, but it's worth investigation and it has that "syntactic sugar" you're craving!
Chris
好吧,这是一个老歌,但似乎没有其他人发布了我认为最好的风格的答案(根据我对您的要求的理解):
重要的一点是
CreateNewTimedMessage
可能作为一个侧面-effect 创建并存储半永久Message
对象,但它返回一个临时计时对象(使用StopWatch
或某种类似机制)它无法在using
块的范围内生存。 (MessageTimer
可以引用Message
,但反之则不行。)因此,处置
MessageTimer
对象可能会产生副作用在某处记录最后的时间,但该对象本身将无法生存或复活; 这并不是对using
结构的滥用,因为您实际上是在处理一个对象。(
MessageTimer
的实际实现可能与 Joe 的答案类似。)Well, this is an oldie, but nobody else seems to have posted the answer which I think is the best style (from what I understand of your requirements):
The important point being that
CreateNewTimedMessage
might as a side-effect create and store a semi-permanentMessage
object, but it returns a temporary timing object (that usesStopWatch
, or some similar mechanism) which doesn't survive the scope of theusing
block. (It's ok for theMessageTimer
to reference theMessage
, but not the reverse.)So disposal of the
MessageTimer
object might have a side-effect of logging the final time somewhere, but the object itself won't survive or be resurrected; this isn't an abuse of theusing
construct because you really are disposing of an object.(The actual implementation of the
MessageTimer
would probably be similar to Joe's answer.)并不真地。 最接近的是这样的(无论如何,这就是 using() 语句背后发生的事情:)
Not really. The closest you can come is something like this (which is what is happening under the hood of the using() statement anyway:)
using 语句的目的是在使用完对象后将其丢弃。 在物体被处理后继续保留它的想法并不完全是有意的。 您可以尝试做一些简单的事情,例如:
The using statement is meant to dispose of objects once you are finished with them. The idea of holding on to an object after it has been disposed is not exactly intended. You might try and do something as simple as:
可能有人会说这是对 using 构造的滥用,因此我可能不会在以这种方式使用 IDisposable 的共享类库中实现公共类。
但我见过这种事情,并且认为如果它保留在应用程序内部就可以了。
我根本不明白这一点。 IDisposable 与 GC 无关,如果您的消息被作为 MessageCollection 的元素引用,那么您的消息将保持活动状态。
您的消息类可能类似于下面的示例。 调用 Dispose 后,它仍然保持活动状态并且运行良好。 大多数实现 IDisposable 的类在调用 Dispose 后将不可用,因此,如果在调用 Dispose 后访问成员,它们的实现将引发 ObjectDisposeException。 但这绝不是强制性的。
It could be argued that this is an abuse of the using construct, so I probably wouldn't implement a public class in a shared class library that uses IDisposable in this way.
But I've seen this kind of thing done and think it is OK if it stays internal to your application.
I don't understand this at all. IDisposable has nothing to do with GC, and your message will stay alive if it's referenced as an element of your MessageCollection.
Your message class might look something like the sample below. After Dispose is called, it remains alive and well. Most classes that implement IDisposable are unusable after Dispose is called, and their implementation therefore throws an ObjectDisposedException if members are accessed after calling Dispose. But this is by no means mandatory.
“using”语句实际上编译为 Try/Finally,期望对象实现 IDisposable——“using”只是该语言提供的快捷方式。
出于您的目的,特别是因为您想回收该对象,我会考虑编写您自己的接口。 当你调用一个方法来开始计时时,只需获取 datetime.now 的值即可; 当你停止计时器时再次。 然后用停止时间减去开始时间,就得到了持续时间。
避免在每个类实例上使用真正的 Timer 对象。 计时器使用线程池线程,这意味着每条消息将至少消耗一个线程——如果系统中的消息太多,您的应用程序将因线程切换而减慢速度。 另外,如果计时器没有正确处理,它们可能不会释放其 ThreadPool 线程,并且本质上会出现线程泄漏。
The 'using' statement actually compiles down to a Try/Finally that expects the object to implement IDisposable--"using" is just a shortcut provided by the language.
For your purposes, especially since you want to recycle the object, I would consider writing your own interface. When you call a method to start timing just grab the value of datetime.now; and again when you stop the timer. Then subtract starttime from stoptime and you'll have the duration.
Avoid using a true Timer object on each class instance. Timer's use a ThreadPool thread, which means that each message will consume at least one thread--if too many messages are in the system your application will slow down as a result of the thread switching. Also, if the timer's aren't properly disposed they may not release their ThreadPool threads and you will essentially have a thread leak.
最初
IDisposable
旨在作为在 C# 中引入确定性清理的一种方式,但我已经看到 作者和实现使用using
/Dispose
功能用于与您正在谈论的内容相关的内容。就我个人而言,我对此不太高兴,因为它破坏了
IDisposable
背后的概念,但由于没有替代方案,所以这取决于您想要成为多么正统的问题。 我当然可以理解您为什么要这样做,但这样做会使解释 IDisposable 接口的目的和重要性变得更加困难。Originally
IDisposable
was intended as a way to introduce deterministic clean-up in C#, but I have seen authors and implementations use theusing
/Dispose
feature for something along the lines of what you are talking about.Personally, I'm not too happy about it as it undermines the concept behind
IDisposable
, but since there's isn't an alternative it comes down to a matter of how orthodox you want to be. I can certainly see why you want to do it, but by doing so you make it harder to explain the purpose and the importance of theIDisposable
interface.再次查看您的问题后,我不明白有问题的对象实际上是一条消息,除非它更像是一条跟踪消息(用于调试帮助)。
如果您正在寻找更多类似的内容,那么这里有一个使用委托的非常粗略方法。 本质上,您为要调用的每个方法和时间创建一个委托,然后将委托和方法参数传递给负责实际调用该方法并计时其持续时间的助手。
我的示例中明显的缺点是我牺牲了类型安全参数,因为我不确定它是否正是您正在寻找的东西。 另一个问题是,每次您想要调用一个方法时,您都需要添加一个新的委托,该方法具有您尚未拥有其委托的方法签名:
After reviewing your question again I don't see how the object in question is really a Message, unless it is more like a tracing message (for debugging assistance).
If you're looking for something more along those lines then here is a very rough approach using delegates. Essentially you create a delegate for each method you want to call and time, then pass the delegate and the method arguments off to a helper who is responsible for the actual calling of the method and timing its duration.
The obvious shortcoming in my example is that I sacrificed type-safe arguments since I wasn't sure if it was exactly what you're looking for or not. The other issue is that you would need to add a new delegate every time you want to call a method which has a method signature that you don't already have a delegate for: