Saturday, October 07, 2006 1:26 AM
bart
Using aliases in C# - Notes on extern alias, the :: operator and even more
Introduction
Today I have a post for you about a feature that's not so well-known as it should be in my opinion: aliases in C#. Everyone should know about the using keyword to import a namespace:
using
System;
...
Console
.WriteLine("Hello"); //instead of System.Console.WriteLine
It doesn't have any runtime implications whatsoever (some small amount of people think that having more namespace imports implies performance dropdown :o), it's just used at compile-time to locate classes. Recall that the CLR doesn't know about namespaces, it just identifies classes by their full name (e.g. System.Collections.Stack).
Basics of namespace imports
But what about having a class with the same (language-level without namespace qualifier) name in multiple namespaces?
namespace
Sample.First
{
public class Bar { }
}
namespace Sample.Second
{
public class Bar { }
}
namespace Demo
{
using Sample.First;
using Sample.Second;
class Program
{
public static void Main()
{
Bar b = new Bar();
}
}
}
Right, the compiler complains: error CS0104: 'Bar' is an ambiguous reference between 'Sample.First.Bar' and 'Sample.Second.Bar'.
So you have to disambiguate by using a full qualified namespace/class notation. Alternatively you can define aliases:
namespace Demo
{
using A = Sample.First;
using B = Sample.Second;
class Program
{
public static void Main()
{
A.Bar b = new A.Bar();
}
}
}
This is a commonly applied technique to abbreviate namespaces, for example in VSTO:
using Outlook = Microsoft.Office.Interop.Outlook;
using Word = Microsoft.Office.Interop.Word;
Aliases for class names
However, it's a common misbelief that this kind of aliasing only works with namespaces. You can use it to create an alias for a class as well, as shown below:
using
System;
using System.Collections.Generic;
using IntList = System.Collections.Generic.List<int>;
using List = System.Collections.ArrayList;
class Program
{
public static void Main()
{
IntList lst = new IntList();
Console.WriteLine(lst.GetType());
List lstc = new List();
Console.WriteLine(lstc.GetType());
List<int> lstg = new List<int>();
Console.WriteLine(lstg.GetType());
}
}
I chose this example to show the possible confusion that can be introduced by this: List is defined as a non-generic ArrayList in this case. However, when using a type parameter it's treated as a generic list variant in System.Collections.Generic. The IntList alias should be pretty clear. Here's the app's output:
System.Collections.Generic.List`1[System.Int32]
System.Collections.ArrayList
System.Collections.Generic.List`1[System.Int32]
Kind of a very lightweight #define equivalent if you want. The only allowed aliases are textual names without dots in it (things like Bar.Foo or Foo<T> won't work).
Extern aliases
Yet another feature are extern aliases. See Robert's comment about the VS2005 IDE support for this (thanks to Robert for pointing this out!). As command-line geeks, we'll stick at the bottom of the stack, enjoying csc.exe fun. Absorb the following scenario:
Library 1 (lib1.dll)
namespace
Bar
{
public class Abc { }
public class Foo { }
}
Library 2 (lib2.dll)
namespace
Bar
{
public class Def { }
public class Foo { }
}
Is this valid to do? Yes of course, since we define a class (coincidentally or not) with the same name "Bar.Foo" in two different assemblies there is no conflict. If you'd compile it into one library the CS0101 error would pop up (telling you have two identical definitions). However, when you start both libraries in one single application, problems appear:
using
Bar;
class Program
{
public static void Main()
{
Foo f = new Foo();
}
}
Using the following command-line compiler invocation:
csc /r:lib1.dll,lib2.dll program.cs
Which Foo should be used? The compiler can't know your intentions. Now assume you haven't created this ugly situation yourself and you find two libraries which contain a class with the same name and you want to use both classes at the same time in the same project (a typical example of Murhpy's Law). The compiler will complain as follows:
error CS0433: The type 'Bar.Foo' exists in both 'c:\temp\lib1.dll' and 'c:\temp\lib2.dll'
There is however a solution to this: extern aliases. Change the program's code as follows:
extern alias Lib1;
extern alias Lib2;
class Program
{
public static void Main()
{
Lib1::Bar.Foo f1 = new Lib1::Bar.Foo();
Lib2::Bar.Foo f2 = new Lib2::Bar.Foo();
}
}
or
extern alias Lib1;
extern alias Lib2;
using Lib1::Bar;
class Program
{
public static void Main()
{
Foo f1 = new Foo();
Lib2::Bar.Foo f2 = new Lib2::Bar.Foo();
}
}
or
extern alias Lib1;
extern alias Lib2;
using Bar1 = Lib1::Bar;
using Bar2 = Lib1::Bar;
class Program
{
public static void Main()
{
Bar1.Foo f1 = new Bar1.Foo();
Bar2.Foo f2 = new Bar2.Foo();
}
}
Compilation should be performed like this:
csc /r:Lib1=lib1.dll /r:Lib2=lib2.dll program.cs
Things learned:
Finally there's also the concept of the "global namespace". For example you can refer to System.Diagnostics.Process from the BCL using the global::System.Diagnostics.Process syntax. This is also a good IntelliSense trick to find out about top-level namespaces that are available:

Have fun!
Del.icio.us |
Digg It |
Technorati |
Blinklist |
Furl |
reddit |
DotNetKicks
Filed under: C# 2.0