I was building an ASP.NET Ajax script control yesterday, and decided I'd try the new and improved ASP.NET Ajax Server Control wizard in Visual Studio 2008. The wizard creates a nice class that inherits from ScriptControl and provides an override of GetScriptDescriptors and GetScriptReferences that wire up the embedded JavaScript file used in the client. However, I noticed something curious about the default implementation of each of these methods: they used yield return as a way of returning a single instance of ScriptDescriptor and ScriptReference respectively as an enumerable collection (of 1). Here's what the override of GetScriptDescriptors() looks like:
protected
override
IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
ScriptControlDescriptor descriptor = new
ScriptControlDescriptor("MyScriptControl.ClientControl1", this.ClientID);
yield
return descriptor;
}
Now in the past, I have always implemented this method by dynamically allocating an array of one element since arrays already support IEnumerable:
protected
override
IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
ScriptControlDescriptor descriptor = new
ScriptControlDescriptor("AjaxServerControl2.ClientControl1", this.ClientID);
return
new
ScriptDescriptor[] { descriptor };
}
When I first saw the use of yield return, I thought "Cool – I hadn't thought of using yield return that way!" It looked cleaner and shinier, without any messy brackets or braces, an elegant pair of keywords replacing a clunky array allocation. Then I began to have second thoughts… might it be less intuitive for someone reviewing the code? Hmm, debatable perhaps, but dynamically allocating an array to return a collection is a pretty common pattern in C#. Using yield return to concoct an enumerator implementation on the fly on top of one object, while pretty slick, may be less intuitive to understand.
But what occurred to me next, was the potential overhead of this technique. Since yield return will build an entirely new class to implement the IEnumerable interface automatically for you each time you use it, the amount of extra code generated by this technique could become rather significant if overused. To see just how much extra code we were looking at, I compiled two different versions of the code (actually a somewhat simplified version for simplicity) using each technique, and then opened each binary up in ildasm to compare the number of lines of IL generated. The array allocation function generated a total of 20 lines of IL, but the yield return function, if you included all of the IL instructions for the IEnumerable class generation as well was over 100! That's a 5x penalty in code generation to save 18 characters of typing J Just to confirm, I then built a small console application with 10 methods in it that return IEnumerable, and implemented all 10 methods with array allocation and then with yield return. The footprint of the release-compiled binary using the array allocation was 16k, and using the yield return method, 25k!
Moral of the story? Just because something looks shiny and new doesn't mean it's better… yield return is a powerful feature of C# that can be invaluable when you need an IEnumerable implementation over dynamic data, but for simple methods like GetScriptDescriptors() I'm going to stick with the tried and true method of array allocation.
Posted
Feb 06 2008, 07:26 AM
by
fritz-onion