Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Understanding Namespaces in C# 2.0

0.00/5 (No votes)
20 Dec 2006 1  
This articles specifies the problems with namespaces in earlier versions of C# and how C# 2.0 handles them

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:

  1. 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.
  2. 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.
  3. 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:

  1. Namespace aliasing
  2. global:: operator
  3. 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.

//replace
//IPassengerCarrier carrier = new Manufacturer.AirBusA340();
//with
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()
            {
                // problem. ambiguous call to Console as it is
                // available in System namespace and MyOrg.System namespace
                Console.WriteLine("Hello");

                // this is also ambiguous
                System.Console.WriteLine("Hello");
                // these code lines will not refer to the top level System.Console
            }
        }
    }
}

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()
            {
                // unambiguous call. Calls Console class
                // available at MyOrg.System namespace
                Console.WriteLine("Hello");

                // this is also unambiguous
                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:

//in assembly - first.dll
namespace MyOrg.Utility
{
    public class Helper
    {
        public static bool SendMail()
        {
            //sends mail through the SmtpServer
        }
    }
}

//in assembly - second.dll
namespace MyOrg.Utility
{
    public class Helper
    {
        public static bool SendMail()
        {
            //sends mail from the mail account of the user
        }
    }
}

//consumer code - references to both first.dll and second.dll
namespace MyOrg.Utility
{
    public class Program
    {
        public static void NotifyUser()
        {
            //this will fail at compile time as the
            //assembly namespaces are colliding
            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:

//consumer code - references to both first.dll and second.dll

//while compiling the following references should be user
//C:\Code>csc /r:First=first.dll /r:Second=second.dll Program.cs

//this can be done from VS 2005, by opening the properties of the
//referenced DLL's properties
//and providing aliases in the Aliases property

extern alias First;        //should match the alias provided in the compiler options
extern alias Second;       //should match the alias provided in the compiler options

using FirstUtility = First::MyOrg.Utility;
using SecondUtility = Second::MyOrg.Utility;

namespace MyOrg.Utility
{
    public class Program
    {
        public static void NotifyUser()
        {
            //this will not fail
            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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here