|
Thanks N a v a n e e t h,
I have found one setback of using block. It will not handle any exception in Dispose method. And if there are any exception in Dispose method, the original exception in using block will be missing.
Here is my test code. Any comments?
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
class Test
{
class MyException : ApplicationException
{
public MyException (string message)
: base(message)
{
}
}
class MyObject : IDisposable
{
public virtual void Dispose()
{
Console.WriteLine("Disposed ");
throw new MyException("Disposed exception. ");
}
}
public static void Main()
{
try
{
using (MyObject myobj = new MyObject())
{
throw new MyException("Hello World");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
regards,
George
|
|
|
|
|
using is a syntactic shortcut for try/finally pattern. I rewrote your code with try/finally for making this clear
try
{
MyObject myobj = new MyObject();
try
{
throw new MyException("Hello World");
}
finally
{
myobj.Dispose();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
} Here it will throw MyException("Hello World") first - since we haven't defined any catch statements, corresponding finally will be executed. In this case finally also throwing error and the catch block will handle the latest exception which is disposed exception here.
The dispose pattern suggests to handle all possible exception and not to throw exceptions in the Dispose method. So you won't feel this problem anymore.
|
|
|
|
|
Thanks N a v a n e e t h!
It is in theory that the information of MyException("Hello World") is missing when there is exception in Dispose, right?
regards,
George
|
|
|
|
|
George_George wrote: It is in theory that the information of MyException("Hello World") is missing when there is exception in Dispose, right?
Not exactly. You can catch the exception before disposing. The behavior you are seeing is how catch works. It catches latest exceptions. As you know finally executes even if there is an error. So you are generating one more exception inside the finally. So the latest exception is the one generated inside the finally block and it will be handled by the next catch statement.
|
|
|
|
|
Thanks N a v a n e e t h,
You always have great ideas.
Your solution works perfect when there is exception and we are in finally block because of the exception. The issue I can see from this solution is, when executing in finally block, there may be no exception, right? How can you write a common finally block, to handle exception at first, then Dispose?
regards,
George
|
|
|
|
|
George_George wrote: when there is exception and we are in finally block because of the exception
We are in finally block not because of exception. Finally block will be executed always.
George_George wrote: How can you write a common finally block, to handle exception at first, then Dispose?
Finally block can't handle exceptions, it is caught by a catch block. I am not getting your point here. Can you explain it little bit more ? Are you asking how to handle exceptions in the finally ?
|
|
|
|
|
Thanks N a v a n e e t h,
Sorry for my bad English, here is my code posted before. And it has the issue of MyException ("Hello World") will be missing if there is exception in Dispose method.
You have a great solution to solve it by -- "You can catch the exception before disposing". It sounds great but I do not know how to apply this idea in my code sample. Could you help to show your code please?
http://www.codeproject.com/script/Forums/View.aspx?fid=1649&msg=2568610[^]
regards,
George
|
|
|
|
|
okay. Here is what you can do
public static void Main()
{
try
{
using (MyObject myobj = new MyObject())
{
try{
throw new MyException("Hello World");
}
catch(MyException)
{
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
} In this case, exception is handled before object is disposing. This is pretty straight forward.
Hope it's clear
|
|
|
|
|
Thanks N a v a n e e t h,
Your solution is good enough.
I have some further ideas. In your pattern there will be additional overhead, since there will be 3 try blocks, two explicit ones and one implicit one.
Try block will add additional overhead in my knowledge to runtime to stack manipulation.
So, if not using "using" block, we only need two try block, one to wrap normal operations and one to wrap Dispose in finally. So, in this way, using block has no advantage -- it uses 3 using blocks and we can only use 2 if write manually.
Any comments?
regards,
George
|
|
|
|
|
You are right. It can be written as you said.
|
|
|
|
|
Great N a v a n e e t h!
Question answered.
regards,
George
|
|
|
|
|
Wait just a freakin minute. Back when I was arguing against using C# and .Net, everybody was quick to point out the automatic-ness of memory handling, specifically mentioning thew GC.
Now, here we are a few years down the road, and I see people saying not to rely on the GC.
So which is it - a good thing, or a bad thing?
Personally, I avoid "using" statements because I think they obfuscate the intent of code.
"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997 ----- "...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
|
|
|
|
|
John Simmons / outlaw programmer wrote: So which is it - a good thing, or a bad thing?
Having a GC is good because it reduces the problem of leaks, etc, but it would be foolish to rely on it to do *all* your cleanup.
Cheers,
Vikram.
The hands that help are holier than the lips that pray.
|
|
|
|
|
I agree, Vikram.
regards,
George
|
|
|
|
|
Thanks John Simmons!
Can you say more about why do you think of this please -- "obfuscate the intent of code"?
regards,
George
|
|
|
|
|
John Simmons / outlaw programmer wrote: automatic-ness of memory handling
Memory handling is automatic, but resource management (file/DB/Kernel objects etc..) is not (if you don't count finalization). The garbage collector will definitely run when the app is running out of memory, but it doesn't know about other resources. Which is why relying on finalization to clean them up is a bad idea - your app might run out of handles (for example) long before the GC decides to run.
John Simmons / outlaw programmer wrote: Personally, I avoid "using" statements because I think they obfuscate the intent of code.
Really? How else do you close DB connections or file streams?
|
|
|
|
|
S. Senthil Kumar wrote: Really? How else do you close DB connections or file streams?
I'm a programmer - I write all the code.
"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997 ----- "...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
|
|
|
|
|
You never know when the GC will finalize the object. In a lot of cases, that's fine; but often it's important to know when an object is disposed.
In the case of a StreamWriter writing to a file, the file is left open until the StreamWriter is disposed. If you leave that to the GC, the file will be left open for an undefined amount of time. If your app (or another app) tries to write to that file again during that time, it will fail because the file is still in use.
Whenever you "lock" some object (file, TCP port, ...), you have to ensure that you unlock it as soon as possible, so you have to call Dispose.
Also, for some other objects there's a limit on the maximum number of objects that the GC doesn't know about (e.g. Brush/Pen allocate GDI objects, and there's a maximum of 10000 GDI objects per process). It might be possible that you hit the limit if you allocate too many such objects between two GC runs. Explicitly disposing brushes/pens solves that problem (although you already have a serious problem if you allocate that many objects; but you were more likely to run into this issue on Windows 98/ME which allowed much less GDI objects).
Also, there are objects that don't get garbage collected if you don't dispose them - e.g. if an object adds itself as a handler for an event on a long-lived object; it doesn't get garbage collected until the event handler is unregistered by the Dispose method.
|
|
|
|
|
Great Daniel!
I have a further question. If there is exception, and in both exception handler and finally block, I do not call Close/Dispose explicitly, will Dispose/Close be called automatically during exception (e.g. similar to during stack unwinding in C++)? Or we have to wait for GC to call Finalize?
regards,
George
|
|
|
|
|
George_George wrote: If there is exception, and in both exception handler and finally block, I do not call Close/Dispose explicitly, will Dispose/Close be called automatically during exception
No. Unless, of course, the control comes out of the using block before the exception is caught. Why don't you try it and see? (Just put a Console.WriteLine in the Dispose block and another in the finally block.)
Cheers,
Vikram.
The hands that help are holier than the lips that pray.
|
|
|
|
|
Sorry, Vikram!
My English is not good. I think you misunderstand my question from your reply below. I mean if I am not using "using" block, what will happen? Will Dispose be called automatically during stack unwind or something if I do not call it explicitly in exception handler block or finally block?
Vikram A Punathambekar wrote: No. Unless, of course, the control comes out of the using block before the exception is caught. Why don't you try it and see? (Just put a Console.WriteLine in the Dispose block and another in the finally block.)
regards,
George
|
|
|
|
|
No.
Cheers,
Vikram.
The hands that help are holier than the lips that pray.
|
|
|
|
|
Thanks for your clarification, Vikram!
regards,
George
|
|
|
|
|
George_George wrote: will Dispose/Close be called automatically during exception (e.g. similar to during stack unwinding in C++)? Or we have to wait for GC to call Finalize?
Dispose() won't be called automatically on exceptions. You have to do execution in try block, catch all possible exceptions, cleanup in finally. using statement is a syntactic shortcut for this pattern.
|
|
|
|
|
Thanks N a v a n e e t h,
Any comments to here? I write more details about my specific situations there. Even though I am not using "using" and since the traffic is low, I do not meet with any issues.
I would like to learn from you and let you anticipate what issues I will meet with if I do not use "using" in my situation?
http://www.codeproject.com/script/Forums/View.aspx?fid=1649&msg=2561393[^]
regards,
George
|
|
|
|