Introduction
Namespace in .NET is an additional information. If provided, it will enable unique naming of types within an assembly. This articles assumes C# knowledge and all the issues/resolutions provided are applicable to C# 1.1/2.0
For example, you can have a class called Console
in your assembly, within a namespace named, say, MyNamespace
. System namespace also has a class named Console
. We can use both classes in the program .
Problems with Namespaces in 1.1
If you worked on a large project in .NET in the past, you must have faced some problems with namespaces. Namespaces were introduced in .NET to eliminate the naming collisions between different code developed at different locations. But as larger projects were developed, some shortcomings were observed. Some of them included:
- Same typename can be used in multiple namespaces. In this scenario, types in local namespaces can mask types in global namespaces. This leads to an ambiguous situation.
- Same typenames can be used in multiple assemblies. When these assemblies were used in the same application, again, there will be ambiguity in referencing the types.
- Using aliases can lead to ambiguous identification. Aliases can mask the type if care is not taken.
Namespaces in 2.0
To address all the issues that arose in the previous version, C# 2.0 introduced some new features like:
- Namespace aliasing
global::
operator
- Extern Aliasing
Namespace Aliasing
With namespace aliasing, parallel namespaces are much more convenient. Namespace aliasing also offers greater flexibility.
Let's look at the following piece of code:
namespace Aeroplane
{
interface IGoodsCarrier
{
}
interface IPassengerCarrier
{
}
namespace Boeing
{
class Boeing737 : IPassengerCarrier { ... }
}
namespace AirBus
{
class AirBusA340 : IPassengerCarrier { ... }
}
}
using Manufacturer = Aeroplace.AirBus;
namespace Aeroplane
{
namespace Company { ... }
class Program
{
static void Main()
{
IPassengerCarrier carrier = new Manufacturer.AirBusA340();
}
}
}
This works well with both 1.1 and 2.0, but if somebody adds:
namespace AeroPlane
{
namespace Manufacturer { ... }
}
1.1 will throw a compile time exception as there is a colliding namespace with the alias qualifier.
2.0 addresses this issue with a minor syntax change. We'll now use ::
operator to resolve the ambiguity.
IPassengerCarrier carrier = new Manufacturer::AirBusA340();
This will unambiguously refer to Aeroplane.AirBus
.
::
operator identifies the left hand side operator of the instruction as an alias and not as a namespace.
global:: Operator
To understand the importance of this operator, let's look at this sample code:
namespace MyOrg
{
namespace System
{
public class Console { ... }
public class Program
{
static void Main()
{
Console.WriteLine("Hello");
System.Console.WriteLine("Hello");
}
}
}
}
global::
operator will unambiguously refer to the top level scope in these situations. Let's check the modified code:
namespace MyOrg
{
namespace System
{
public class Console { ... }
public class Program
{
static void Main()
{
Console.WriteLine("Hello");
global::System.Console.WriteLine("Hello");
}
}
}
}
External Aliasing
If you are working in a large organization, you might have observed that developers working on two different assemblies define the same typename in the same namespace, most of the times in Utility
classes.
Let's look at this code to understand this situation:
namespace MyOrg.Utility
{
public class Helper
{
public static bool SendMail()
{
}
}
}
namespace MyOrg.Utility
{
public class Helper
{
public static bool SendMail()
{
}
}
}
namespace MyOrg.Utility
{
public class Program
{
public static void NotifyUser()
{
Helper.SendMail();
}
}
}
External aliasing can resolve ambiguous references to the namespaces. External aliasing provides assembly qualified namespace aliases. extern
is the keyword that should be used to provide the assembly qualified namespaces.
Defining the extern
alias involves two things. Firstly, we need to define the extern
alias in the source code of the consumer and we also need to map the aliases to the assemblies through the compiler options.
Let us look at how the above program changes:
extern alias First;
extern alias Second;
using FirstUtility = First::MyOrg.Utility;
using SecondUtility = Second::MyOrg.Utility;
namespace MyOrg.Utility
{
public class Program
{
public static void NotifyUser()
{
FirstUtility.Helper.SendMail();
SecondUtility.Helper.SendMail();
}
}
}
Summary
Though these things seem to be very uncommon situations, in large projects, these are quite common and sometimes very frustrating. Now, we have these changes in C# 2.0 which will help working with large projects without frustration.
History
- 20th December, 2006: Initial post