Jeff Atwood had a great post a while back called "Strong Opinions, Weakly Held." It's good to feel strongly about something, but important to be open to changing your opinion if you're faced with new evidence.
Last week a reader pointed me to this post at the Microsoft StyleCop blog that shows some interesting examples of using directives outside and inside the namespace declaration.
For example, this compiles fine:
using Guid = System.Guid;namespace Microsoft.Sample{ public class Guid { public Guid(string s){} } public class Program { public static void Main(string[] args) { Guid g = new Guid("hello"); } }}
However this one with the using moved inside the namespace doesn't compile:
namespace Microsoft.Sample{ using Guid = System.Guid; public class Guid { public Guid(string s){ } } public class Program { public static void Main(string[] args) { Guid g = new Guid("hello"); } }}
The code fails on the following compiler error, found on the line containing Guid g = new Guid("hello");
CS0576: Namespace 'Microsoft.Sample' contains a definition conflicting with alias 'Guid'
In the first example, there's an alias created, but it doesn't matter because the second Guid class is in local scope (there's no scope conflict) and the compiler chooses the inner Guid class.
In the second example, there are two "Guids" declared in the same scope and that's a conflict that the compiler can't resolve automatically. The style rule/argument the post makes is that you will only see these kinds of conflicts if you put your using directives inside your namespaces. To this, I say, "meh." Sure, if it makes you happy and you use lots of namespace aliases, sure, but it's an edge case. I simply prefer to have my namespaces outside.
However, the second rule in the post said:
"However, placing the using statements [Ed. Note: They mean "directives"] within a namespace element allows the framework to lazy load the referenced assemblies at runtime. In some cases, if the referencing code is not actually executed, the framework can avoid having to load one or more of the referenced assemblies completely. This follows general best practice rule about lazy loading for performance."
This stopped me in my tracks. This rocks the very bedrock that my knowledge of the CLR stands on. I'm like, NO WAY, and then I oscillated back and forth between denial and acceptance. Then, I settled on denial. I don't buy it. A using directive is for aliasing and is a kind of syntactic sugar. Ultimately the IL is the same. Assembly loading won't be affected as the assembly manifest doesn't change.
Here's what my experiment showed. I believe it's true until I find out from someone on the CLR Loader team that it's not true. ;)
using System;using System.Xml;namespace Microsoft.Sample{ public class Program { public static void Main(string[] args) { Guid g = Guid.NewGuid(); Console.WriteLine("Before XML usage"); Console.ReadLine(); Foo(); Console.WriteLine("After XML usage"); Console.ReadLine(); } public static void Foo() { XmlDocument x = new XmlDocument(); } }}
I ran this program outside the debugger but compiled in debug mode. At the point there the first ReadLine() hits, the program pauses and waits for an Enter key. I loaded up Process Explorer and saw:
Then, I hit Enter, executing the Foo() method and new'ing up an XmlDocument. You can see that System.Xml just got loaded (specifically the native image) into the process.
If I do the same thing with the usings INSIDE the namespace I get identical results.
namespace Microsoft.Sample{ using System; using System.Xml; public class Program { public static void Main(string[] args) { Guid g = Guid.NewGuid(); Console.WriteLine("Before XML usage"); Console.ReadLine(); Foo(); Console.WriteLine("After XML usage"); Console.ReadLine(); } public static void Foo() { XmlDocument x = new XmlDocument(); } }}
In fact, the only thing that changed the way the assemblies got loaded was switching to release mode. Running the app in release mode had all the assemblies in my trivial app loaded immediately. I thought it was weird for a second, but then realized it had nothing to do with debug vs. release. It was simply that the Foo() method was either inlined or there was a Tail Call Optimization as I explored in this post: Release IS NOT Debug: 64bit Optimizations and C# Method Inlining in Release Build Call Stacks.
I'm 99.99% sure at this point that using directives can't change your assembly loading behavior and I think I was right to be suspicious. However, I'm going to ask some people on the Fusion (assembly loader) and C# teams who are smarter than I and I'll update this post as I learn more!
However, the Back to Basics Tips here are:
And be ready to be wrong anyway! It only takes one negative experiment to disprove a theory. Of course, the real question is, what does the specification say?
UPDATE #1: Ian Griffiths had a similar reaction and a similar test!
What do you think, Dear Reader?
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.
Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.