Re: StreamReader/StreamWriter
Quote:
Originally Posted by Arjay
Of course this depends on how the developer has implemented it. A developer would have to be a bit brain dead not to release resources (i.e. internally call Close()) before disposing themselves since it's the point of Dispose..
Close but not exactly...
The "standard" is that Dispose() should do the WORK, and it is proper to create meaningful ALIAS's for Dispose that make bettersymantic sense.
Thus, (providing an instance does NOT support "re-opening", then Close() should in fact call Dispose(), and NOT the other way around.
Re: StreamReader/StreamWriter
Quote:
Originally Posted by George2
....
will be expanded by C# compiler like this,
Code:
try
{
srObject.Open() ...
read stuff etc...
}
catch
{
any exceptions ...
}
finally
{
srObject.close()
srObject.dispose()
}
There are two issues from the code,
- You can see all exceptions are eaten. From outside you can not catch any more exceptions -- e.g. write a log or something. The exception is not rethrown. :-)
- In finally block, no exception handled for Dispose/Close method.
No George. Sure the exceptions are NOT eaten. You need to handle them yourself. I'm using 'using' very often and use the upcoming exceptions for example for testing if the user has connecteto the correct database or if his password was wrong and all that. Per sure I have tested the code if it fires when pwd is wrong and all that. It works. So your idea is wrong in this way.
Re: StreamReader/StreamWriter
Quote:
Originally Posted by TheCPUWizard
Close but not exactly...
The "standard" is that Dispose() should do the WORK, and it is proper to create meaningful ALIAS's for Dispose that make bettersymantic sense.
Thus, (providing an instance does NOT support "re-opening", then Close() should in fact call Dispose(), and NOT the other way around.
This may be one case, but I don't believe it's the norm (at least with the IDisposable implementations that I'm familiar with).
For example, using reflector, here's the Dispose code for SqlConnection.
Code:
protected override void Dispose(bool disposing)
{
if (disposing)
{
this._userConnectionOptions = null;
this._poolGroup = null;
this.Close();
}
this.DisposeMe(disposing);
base.Dispose(disposing);
}
On the other hand, using reflector again, the Close() method doesn't call Dispose() (and as well it shouldn't).
The assembly I checked was System.Data.dll, version 2.0.0.0
I don't know if earlier versions did something different (if I recall, ver 1.0 didn't implement IDisposable). I suspect the later versions follow the same pattern as 2.0.
Re: StreamReader/StreamWriter
Thanks Arjay,
I agree with your points about Close/Dispose. What I mean is, using block is not smart enough to call Close other than call Dispose. It will always call Dispose. :-)
Quote:
Originally Posted by Arjay
Of course this depends on how the developer has implemented it. A developer would have to be a bit brain dead not to release resources (i.e. internally call Close()) before disposing themselves since it's the point of Dispose.
Fortunately, the .net class libraries I use seem to do this. SqlConnection, SqlCommand, File I/O (readers, writers), Message Queue all seem to do it.
It's just good practice.
regards,
George
Re: StreamReader/StreamWriter
Thanks TheCPUWizard,
In my experience to learn code from books and MSDN, Close is just an alias for Dispose, and the function is always the same. Usually, Close just have one line of code, which calls Dispose.
Anyway, could you show us a case when the function of Close and Dispose are different please? :-)
Quote:
Originally Posted by TheCPUWizard
Close but not exactly...
The "standard" is that Dispose() should do the WORK, and it is proper to create meaningful ALIAS's for Dispose that make bettersymantic sense.
Thus, (providing an instance does NOT support "re-opening", then Close() should in fact call Dispose(), and NOT the other way around.
regards,
George
Re: StreamReader/StreamWriter
Thanks JonnyPoet,
I have tested that for point 1, I am wrong. :blush:
But for point 2, from testing I think I am correct. In finally block generated automatically by using block, no exception handled for Dispose/Close method -- so the original exception will be missing.
Here is my code to verify, you can see MyException("Hello World") is missing. :-)
Any comments?
Code:
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());
}
}
}
Quote:
Originally Posted by JonnyPoet
No George. Sure the exceptions are NOT eaten. You need to handle them yourself. I'm using 'using' very often and use the upcoming exceptions for example for testing if the user has connecteto the correct database or if his password was wrong and all that. Per sure I have tested the code if it fires when pwd is wrong and all that. It works. So your idea is wrong in this way.
regards,
George
Re: StreamReader/StreamWriter
Hi Arjay,
Quote:
Originally Posted by Arjay
On the other hand, using reflector again, the Close() method doesn't call Dispose() (and as well it shouldn't).
I am also using reflector to see .Net 2.0 code for SQL.Data.SqlClient.SqlConnection. I found in the finally block, Dispose is called. Here is my code from reflector. Any comments?
Code:
public override void Close()
{
IntPtr ptr;
Bid.ScopeEnter(out ptr, "<sc.SqlConnection.Close|API> %d#", this.ObjectID);
try
{
SqlStatistics statistics = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
statistics = SqlStatistics.StartTimer(this.Statistics);
lock (this.InnerConnection)
{
this.InnerConnection.CloseConnection(this, this.ConnectionFactory);
}
if (this.Statistics != null)
{
ADP.TimerCurrent(out this._statistics._closeTimestamp);
}
}
catch (OutOfMemoryException exception3)
{
this.Abort(exception3);
throw;
}
catch (StackOverflowException exception2)
{
this.Abort(exception2);
throw;
}
catch (ThreadAbortException exception)
{
this.Abort(exception);
throw;
}
finally
{
SqlStatistics.StopTimer(statistics);
}
}
finally
{
SqlDebugContext context = this._sdc;
this._sdc = null;
Bid.ScopeLeave(ref ptr);
if (context != null)
{
context.Dispose();
}
}
}
regards,
George
Re: StreamReader/StreamWriter
Look carefully at what is being disposed here. It's not the SqlConnection object, it's a member field _sdc of type SqlDebugContext.
Re: StreamReader/StreamWriter
Thanks Arjay,
You are correct. But what about the issue of original exception information missing when there is exception in Dispose method?
You can see in my below code. MyException("Hello World") is missing when there is exception in Dispose method.
Code:
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());
}
}
}
Quote:
Originally Posted by Arjay
Look carefully at what is being disposed here. It's not the SqlConnection object, it's a member field _sdc of type SqlDebugContext.
regards,
George
Re: StreamReader/StreamWriter
Quote:
Originally Posted by Arjay
On the other hand, using reflector again, the Close() method doesn't call Dispose() (and as well it shouldn't).
You didn't read a critical part of my post...
Quote:
Originally Posted by TheCPUWizard
Thus, (providing an instance does NOT support "re-opening", then Close() should in fact call Dispose(), and NOT the other way around.
A SqlConnection can be Opened and Closed multiple times.
Look at other classes which do NOT allow "opening" after a call to close...
Quote:
Originally Posted by MSDN
The Close method is a synonym for Dispose (which Message also implements). The message also disposes the object that was used to construct the body when it is disposed.
http://msdn.microsoft.com/en-us/libr...se(VS.95).aspx
Re: StreamReader/StreamWriter
Cpu, I did read that part - that's why I responded with "it's not the norm".
The example that you cited Message.Close( ) as part of the preliminary documentation for the Silverlight class library also implements IDisposable.
As such, I would create it within a using block and call it a day.
Code:
using( Message msg = Message.CreateMessage( ... ) )
{
// do something with the message
}
In my opinion, explicitly calling Close() just clutters up the code.
Re: StreamReader/StreamWriter
Quote:
Originally Posted by Arjay
Code:
using( Message msg = Message.CreateMessage( ... ) )
{
// do something with the message
}
In my opinion, explicitly calling Close() just clutters up the code.
100% agreement. But this is ONLY (IMHO) because of the using statement....
Remember there are ALOT of CLR based languages (my list is over 130 compilers). Many of these do not even expose the concept of exceptions to the developer. So the usage of an "alias" suuch as Close() may make sense in those circumstances.
For C#, the "rule" (enforced by my firm), is that the allocation must be done inside a using statement, or there must be specific (and docummented) reason why that pattern should not be followed. There should not be any explicit calls to Dispose() (or any alias's of Dispose()).
Re: StreamReader/StreamWriter
Quote:
Originally Posted by TheCPUWizard
100% agreement. But this is ONLY (IMHO) because of the using statement....
Remember there are ALOT of CLR based languages (my list is over 130 compilers). Many of these do not even expose the concept of exceptions to the developer. So the usage of an "alias" suuch as Close() may make sense in those circumstances.
For C#, the "rule" (enforced by my firm), is that the allocation must be done inside a using statement, or there must be specific (and docummented) reason why that pattern should not be followed. There should not be any explicit calls to Dispose() (or any alias's of Dispose()).
Sounds good to me. :thumb:
Re: StreamReader/StreamWriter
Quote:
Originally Posted by George2
Code:
try{
using (MyObject myobj = new MyObject()){
throw new MyException("Hello World");
}
}
catch (Exception ex){
Console.WriteLine(ex.ToString());
}
...
George this is totaly clear in my eyes as your Hallo World exception is eaten up by your exception you have thrown in the Dispose method. I did something like the following:
Code:
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsApplication1 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
try {
using (MyObject myobj = new MyObject()) {
string name = myobj.ToString();
throw new Exception("Hello World");
}
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
}
}
public class MyObject:IDisposable {
public override string ToString() {
return "Hallo this is named 'MyObject'";
}
#region IDisposable Member
public void Dispose() {
Console.WriteLine("MyObject said: I'm Disposed");
// throw Exeption("Dispose Exception");
}
#endregion
}
}
Thus you will see the exception will be thrown. But when you additional throw an exception in the dispose method then your first ("HalloWorld") Exception isn't shown. My question is when there will happen an exception in the dispose method ? In my eyes errors during Disposing an object should never occure or should be handled within the Dispose method and then you have
Code:
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
namespace ConsoleApplication2 {
class Program {
static void Main(string[] args) {
try
{
using (MyObject myobj = new MyObject())
{
Console.WriteLine(myobj.ToString());
throw new MyException("inner exception");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
class MyException : ApplicationException {
public MyException (string message) : base(message){
}
}
class MyObject : IDisposable {
public virtual void Dispose()
{
try {
Console.WriteLine("Disposed ");
throw new MyException("Disposed exception. ");
} catch {
Console.WriteLine("Disposed Error Handled");
}
}
public override string ToString() {
return "This is MyObject";
}
}
}
}
And this will show you each exception. Add more Practice, not theory George ! :D ;)
Re: StreamReader/StreamWriter
Thanks TheCPUWizard!
1.
Quote:
Originally Posted by TheCPUWizard
Remember there are ALOT of CLR based languages (my list is over 130 compilers). Many of these do not even expose the concept of exceptions to the developer. So the usage of an "alias" suuch as Close() may make sense in those circumstances.
Because of Close never throws any exception?
2.
Quote:
Originally Posted by TheCPUWizard
For C#, the "rule" (enforced by my firm), is that the allocation must be done inside a using statement
I doubt this. I think we only need a reference type variable in the using statement. You can see similar to Arjay's Message creation sample, we can return an existing reference, no need to allocate (new) something. Any comments?
3.
Quote:
Originally Posted by TheCPUWizard
There should not be any explicit calls to Dispose() (or any alias's of Dispose()).
Because of all Dispose is automatically generated by using block?
4.
Quote:
Originally Posted by TheCPUWizard
I think the difference between Dispose and Close is, when we call Close, it is possible to call Open to re-open. but when we call Dispose, we can not re-open it, right?
regards,
George