C# Tips
C# Tips
02/06/2008 07:05:25 AM
C#
C# Tips: Keep it Short, Get it Done Contributed by David Fells 20060130
Nalpeiron offers low cost, flexible solutions for application licensing. Download a FREE 16page guide on copy protection. Click here
If your goal is to write the most effective code possible, you will want to read this article. David Fells discusses three oftenneglected C# language constructs that help you write as few lines of code as possible to accomplish your tasks. Introduction Developers today have a fairly generous selection of object oriented languages to choose from, and most developers end up learning more than one of them at one time or another. As a result, a lot of developers never learn about the lower level workings of those languages. The .NET runtime and its innards are well beyond the scope and purpose of this article. In fact, this article assumes that readers know the basics of how IL is generated and run. One of the beauties of modern languages like C# is that developers no longer have to worry about memory management and garbage collection. This does not, however, mean that developers should ignore them and become complacent we should always strive to write the most effective code possible. When I say effective, what I am referring to is this: writing the fewest lines of code possible while utilizing the built in language features to complete a task. In this article we are going to examine three C# language constructs that are, in my observations, neglected by most developers. We will first look at the using statement, which provides a form of try/catch structure with improved garbage disposal. In conjunction with the using statement we will take a look at the standard Dispose pattern (implementing the IDisposable interface) We will examine the other two constructs, is and as, together. The is keyword provides a native mechanism for learning an objects type, and as provides a safe, concise type cast. Generally speaking, you do not need to worry about disposal. The main exception, however, is when you are using an unmanaged resource like a database connection or a flat file or any other resource, except for memory. The .NET framework provides the IDisposable interface for this purpose. Since there is no way to guarantee that the Dispose() method gets called, it is considered a best practice to wrap a call to the Dispose() method in an objects finalizer (destructor). When the garbage collector runs, all objects with a finalizer are left in memory and have their finalizers executed. Objects without finalizers are simply deleted from memory which is where unmanaged resources pose the potential for leakage. Here is the IDisposable interface: public interface IDisposable { void Dispose(); } The following code illustrates the basic usage of the IDisposable interface. public class MyClass : IDisposable { 1/5
ASP Free private bool _isDisposed = false; ~MyClass() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(true); } protected virtual void Dispose(bool isDisposing) { if (_isDisposed) return; if (isDisposing) { // Free managed resources } // Free unmanaged resources here _isDisposed = true; } }
02/06/2008 07:05:25 AM
You may be wondering why there is a virtual overloaded version of the Dispose() method in the code above. We created the virtual method to be used as a hook for derived classes to call the base classes disposal method, thus making sure that all resources are properly released. The most important thing to remember when implementing disposal is that you should only be freeing resources in the Dispose() method, nothing else. Do not call any object methods or create references to the current object or do anything else that could effectively resurrect that object. Remember, you never know in what order objects will be disposed, so you may end up working on an object that is already disposed or finalized. Doing that puts the object in question back into a global list of objects, but the garbage collector will not know to dispose of it again because its already been done! The using statement is one of the best ways to see to it that resources are properly disposed of. This construct effectively provides a try/finally statement in which the object referenced directly by the using() clause is explicitly disposed of in the finally block. The following two examples will produce basically the same IL and have the same effect. MyClass obj = null; try { obj = new MyClass(); obj.DoSomething(); } finally { if (null != obj) obj.Dispose(); 2/5
02/06/2008 07:05:25 AM
In both cases, obj is local to the block in question, though it is clear that using requires writing less code and spares us the trouble of explicitly disposing of that object. Remember, even though the garbage collector should be able to rely on your object finalizers as a hook for the Dispose() method, you should still code defensively and be certain that objects are properly disposed of yourself whenever possible. The using statement can be nested just like try statements. For example, suppose you are trying to create a DataReader to bind to an ASP.NET grid. For the sake of brevity, nonrelevant code is omitted. protected DataGrid MyGrid; private void Page_Load(object sender, System.EventArgs e) { using (SqlConnection dbCxn = new SqlConnection(connection string)) { dbCxn.Open(); string sql = SELECT * FROM MyTable; using (SqlCommand dbCmd = new SqlCommand(dbCxn, sql)) { using (DataReader reader = dbCmd.ExecuteReader(CommandBehavior.CloseConnection)) { MyGrid.DataSource = reader; MyGrid.DataBind(); } } } } In this example, if we encounter failure at any point while connecting to the database, querying the database, or binding the data, the grid will simply not be databound. All of the objects will be properly disposed of when the using statement is exited. Exiting the statement happens when all the code inside the statement has run or when an exception is thrown. Can you see a potential problem with the using statement? What if you try to use an object that does not support the IDisposable interface? Well, you simply cannot do that. To use an object in a using statement, that object must implement IDisposable. I am referring to the object appearing in the actual using () portion, not in the inner block code; you can do whatever you like there. Note that you do not have to define the object in the using () block, but I recommend it. Unless for some reason you need to use the object outside the scope of the using statement, always declare and initialize the object within the using () block itself. 3/5
ASP Free
02/06/2008 07:05:25 AM
The as operator provides a semantically logical and programmatically safe type cast. One of the more common situations requiring explicit casts is when working with ASP.NET web controls and navigating the control hierarchy. In the following example, we need to convert a web controls Parent reference to its proper type, in this case a DataGridItem. Using Cstyle casts: try { DataGridItem d = (DataGridItem)obj.Parent; if (d.GetType() == DataGridItem) { // Do something with d } } catch (InvalidCastException e) { } Using as
DataGridItem d = obj.Parent as DataGridItem; if (null != d) { // Do something with d } Always use as in place of the traditional Cstyle cast. Do not use as in place of casting methods exposed by the object itself unless you are certain of the result. For example, you should not use object as string when you could use object.ToString() because it is assumed that the object knows better how to cast itself than the runtime. The is operator is a native language construct that exposes an objects type. is spares one method call in IL by avoiding the call to Object.GetType(). If you are familiar with Visual Basic, you will recognize this operator. Using the example for the as operator, consider this statement: DataGridItem d = obj.Parent as DataGridItem; if (d is DataGridItem) { // Do something with d } The difference, as you probably noticed, is that in the as example d is compared for inequality against null, whereas in the is example it was compared against the type in question. Using is is more intuitive, more readable, and slightly more efficient, making it by far the better choice.
4/5
ASP Free
02/06/2008 07:05:25 AM
Using is and as result in fewer lines of IL and fewer lines of actual C# code by avoiding a lot of extraneous checks and statements, which is evident from the examples above. Conclusion There are always plenty of other ways to improve your code and I encourage you to pursue them. The contents of this article, while useful, are only a piece of the puzzle. In this article we looked at a couple of basic ways to clean up your C# code. First we reviewed object disposal and the IDisposable interface and how it can be used to clean up unmanaged resources. Second, we talked about the using statement, which guarantees proper object disposal. Then we talked about the is and as operators. as provides a safe type cast and is provides a safe type check. The .NET framework provides solid garbage collection and IL optimization, but remember to help out when you can by making sure unmanaged resources are handled and that your objects are disposed of properly. In a managed environment you only have to worry about disposing of types that contain unmanaged resources, or types that are derived from types that implement the IDisposable interface. Also be sure to choose operators and statements that allow you to write fewer lines of code and write code that is more readable and more intuitive. Always keep these things in mind when coding in C#.
DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ realworld tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.
5/5