In AutoCAD .NET sample code no matter where it comes from, the using statement is used more and more popularly. It is good as it makes object memory management code look more structural with less code at the same time.
The reason behind this is that when the object variable goes out of the using statement scope not matter whether everything goes smoothly or something bad happens, the object will get disposed of automatically. We do not have to explicitly call the Object.Dispose() method anymore and more importantly no need to determine when is the good time to do so.
Of course, it does not necessary mean that we have to use using statement always, but the fact is if we do not situations will become more complex to handle.
Let us look at some test commands each of which addresses a different scenario below. One uses using statement and everything goes fine; another also uses using but something goes wrong; the third does not use using but no exception occurs; the final one does not use using either and an exception is thrown out on purpose.
[CommandMethod("TestUsingStatement3")]
public static void TestUsingStatement3_Method()
{
Database db = HostApplicationServices.WorkingDatabase;
Editor ed = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor;
Matrix3d ucs = ed.CurrentUserCoordinateSystem;
Circle cir = null;
Transaction tr = null;
try
{
Point3d center = new Point3d(1, 1, 0);
double radius = 2.0;
cir = new Circle(center, Vector3d.ZAxis, radius);
cir.TransformBy(ucs);
tr = db.TransactionManager.StartTransaction();
throw new System.Exception("An exception for test purpose!");
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
btr.AppendEntity(cir);
tr.AddNewlyCreatedDBObject(cir, true);
tr.Commit();
cir.Dispose();
tr.Dispose();
}
catch (System.Exception ex)
{
ed.WriteMessage(ex.Message);
}
finally
{
ed.WriteMessage("\nHas the circle been disposed of? {0}", cir.IsDisposed);
ed.WriteMessage("\nHas the transaction been disposed of? {0}", tr.IsDisposed);
}
}
[CommandMethod("TestUsingStatement2")]
public static void TestUsingStatement2_Method()
{
Database db = HostApplicationServices.WorkingDatabase;
Editor ed = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor;
Matrix3d ucs = ed.CurrentUserCoordinateSystem;
Circle cir = null;
Transaction tr = null;
try
{
Point3d center = new Point3d(1, 1, 0);
double radius = 2.0;
cir = new Circle(center, Vector3d.ZAxis, radius);
cir.TransformBy(ucs);
tr = db.TransactionManager.StartTransaction();
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
btr.AppendEntity(cir);
tr.AddNewlyCreatedDBObject(cir, true);
tr.Commit();
cir.Dispose();
tr.Dispose();
}
catch (System.Exception ex)
{
ed.WriteMessage(ex.ToString());
}
finally
{
ed.WriteMessage("\nHas the circle been disposed of? {0}", cir.IsDisposed);
ed.WriteMessage("\nHas the transaction been disposed of? {0}", tr.IsDisposed);
}
}
[CommandMethod("TestUsingStatement1")]
public static void TestUsingStatement1_Method()
{
Database db = HostApplicationServices.WorkingDatabase;
Editor ed = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor;
Matrix3d ucs = ed.CurrentUserCoordinateSystem;
Circle cir = null;
Transaction tr = null;
try
{
Point3d center = new Point3d(1, 1, 0);
double radius = 2.0;
using (cir = new Circle(center, Vector3d.ZAxis, radius))
{
cir.TransformBy(ucs);
using (tr = db.TransactionManager.StartTransaction())
{
throw new System.Exception("An exception for test purpose!");
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
btr.AppendEntity(cir);
tr.AddNewlyCreatedDBObject(cir, true);
tr.Commit();
}
}
}
catch (System.Exception ex)
{
ed.WriteMessage(ex.Message);
}
finally
{
ed.WriteMessage("\nHas the circle been disposed of? {0}", cir.IsDisposed);
ed.WriteMessage("\nHas the transaction been disposed of? {0}", tr.IsDisposed);
}
}
[CommandMethod("TestUsingStatement")]
public static void TestUsingStatement_Method()
{
Database db = HostApplicationServices.WorkingDatabase;
Editor ed = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor;
Matrix3d ucs = ed.CurrentUserCoordinateSystem;
Circle cir = null;
Transaction tr = null;
try
{
Point3d center = new Point3d(1, 1, 0);
double radius = 2.0;
using (cir = new Circle(center, Vector3d.ZAxis, radius))
{
cir.TransformBy(ucs);
using (tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
btr.AppendEntity(cir);
tr.AddNewlyCreatedDBObject(cir, true);
tr.Commit();
}
}
}
catch (System.Exception ex)
{
ed.WriteMessage(ex.Message);
}
finally
{
ed.WriteMessage("\nHas the circle been disposed of? {0}", cir.IsDisposed);
ed.WriteMessage("\nHas the transaction been disposed of? {0}", tr.IsDisposed);
}
}
If we run these commands one by one in AutoCAD, we will get the command output as follows:
TestUsingStatement
Has the circle been disposed of? True
Has the transaction been disposed of? True
Command: TESTUSINGSTATEMENT1
An exception for test purpose!
Has the circle been disposed of? True
Has the transaction been disposed of? True
Command: TESTUSINGSTATEMENT2
Has the circle been disposed of? True
Has the transaction been disposed of? True
Command: TESTUSINGSTATEMENT3
An exception for test purpose!
Has the circle been disposed of? False
Has the transaction been disposed of? False
As can be seen, the first two commands which use using statement will get the circle and the transaction objects always disposed of regardless success or failure, but the last command which does not use using statement and has a chance to generate an exception will have potential serious problems since the circle and the transaction objects do not get a chance to be disposed of. The explicit Dispose() calls cannot be reached in this situation!
Of course, if we could make sure nothing bad would happen before the Dispose() calls things would be fine too. Otherwise, moving the Dispose() calls to the ‘finally’ block is another solution though it makes the code look not so structural and we have to declare those object various in unnecessary wider scopes and assign them some default values to make the compiler happy.
So another good AutoCAD .NET programming practice comes out, using using statement when some objects need to be disposed of in a certain scope.
The leading edge AutoCAD .NET Addin Wizard (AcadNetAddinWizard) exercises the good practice in its various wizards, coders and widgets when applicable
Posted by: |