Defining Custom Namespace

When you build larger application with many types, it can be helpful to group your related types into custom namespaces. In C#, this is accomplished using the "namespace" keyword. Explicitly defining custom namespaces is even more important when creating shared assemblies, as other developers will need to reference the library and import your custom namespaces to use your types.


Guidance: While you are free to use any name you choose for your namespaces, the naming convention is typically similar to companynameCompanyName.ProductName.AssemblyName.Path.


// Circle.cs
namespace CustomNamespaces.MyShapes
 {
     public class Circle
     {
         /* Interesting methods... */
     }
 }
// Hexagon.cs
namespace CustomNamespaces.MyShapes
 {
    public class Hexagon
     {
         /* Interesting methods... */
     }
 }
// Square.cs
namespace CustomNamespaces.MyShapes
 {
     public class Square
     {
         /* Interesting methods... */
     }
 }


One of the updates in C# 10 is the addition of file-scoped namespaces. This eliminates the need for opening and closing curly braces wrapping the contents.


namespace CustomNamespaces.MyShapes;
public class Circle 
 {
/* Interesting methods... */
 }


When another namespace wants to use types in a separate namespace, you use the "using" keyword, just as you would when using namespaces of the .NET base class libraries, as follows:


using CustomNamespaces.MyShapes;
Hexagon hexagon = new Hexagon();
Circle c = new Circle();
Square s = new Square();


Resolving Name Clashes with Fully Qualified Names

Technically speaking, you are not required to use the C# "using" keyword when referring to types defined in external namespaces. You could use the fully qualified name of the type.


CustomNamespaces.MyShapes.Hexagon h = new CustomNamespaces.MyShapes.Hexagon();


In fact, in CIL code, types are always defined with the fqdn. In this light, the C# "using" keyword is simply a typing time-saver.


Assume you have a new namespace termed CustomNamespaces.My3dShares, which defines the following 3 classes, capable of rendering a shape in stunning 3D:

namespace CustomNamespaces.My3DShapes
 {
     public class Circle
     {
         /* Interesting methods... */
     }
     public class Hexagon
     {
         /* Interesting methods... */
     }
     public class Square
     {
         /* Interesting methods... */
     }
 }


Ambiguites found!

using CustomNamespaces.MyShapes;
using CustomNamespaces.My3DShapes;


Which namespace do I reference ?

Hexagon h = new Hexagon();
Circle c = new Circle();
Square s = new Square();


The ambiguity can be resolved using the type's fully qualified name;

CustomNamespaces.My3DShapes.Hexagon h = new();


Resolving Name Clashes with Aliases

The C# "using" keyword also lets you create an alias for a type's fqdn. When you do so, you define a token that is substituted for the type's full name at compile time.


using CustomNamespaces.MyShapes;
using CustomNamespaces.My3DShapes;
 
// Resolve the ambiguity for a type using a customer alias.
using The3DHexagon = My3DShapes.Hexagon;
 
// This is really creating a My3DShapes.Hexagon class.
 The3DHexagon h2 = new The3DHexagon();


There is another (more commonly used) using syntax that lets you create an alias for a namespace instead of a type.


using ThreeD = CustomNamespaces.My3DShapes;
 ThreeD.Hexagon h2 = new ThreeD.Hexagon();



Creating Nested Namespaces

namespace CustomNamespaces
 {
     namespace MyShapes
     {
         public class Circle
         {
       }
     }
 }


The second option, using C# 10, uses a file-scoped namespace.


namespace CustomNamespaces;
namespace MyShapes
 {
     public class Circle
     {
    }
 }

The third option (and more commonly used) is to use "dot notation" in the namespace definition.


namespace CustomNamespaces.MyShapes;
public class Circle { }


Change the Root Namespace Using VS 2022

Project Properties -> Application / General Tab -> Default Namespace


Change the Root Namespace Using the Project File

  1. Open *.csproj file.
  2. Update the main PropertyGroup by adding RootNamespace node;


<PropertyGroup>

...

...

<RootNamespace>CustomNamespaces</RootNamespace>

</PropertyGroup>


The Role of .NET Assemblies


.NET applications are constructed by piecing together any number of assemblies.

Simply put, an assembly is a versioned, self-describing binary file hosted by the .NET Runtime.


Assemblies Promote Code Reuse

A code library (also termed a class library) is a *.dll that contains types intended to be used by external applications.


It's perfectly possible for an executable assembly to use types defined within an external executable file. In this light, a referenced *.exe can also be considered a code library.


Regardless of how a code library is packaged, the .NET platform allows you to reuse types in a language-independent manner. For ex, you could create a code library in C# and reuse that library in any other .NET programming language.


Assemblies Are Versionable Units

.NET assemblies are assigned a 4-part numerical version number of the form <major>.<minor>.<build>.<revision>


If you don't explicitly provide a version number, the assembly is automatically assigned a version of 1.0.00, given the default .NET project settings.



Assemblies Are Self-Describing

Assemblies are regarded as self-describing, in part because they record in the assembly's manifest every external assembly they must be able to access to function correctly. Manifest is a blob of metadata that describes the assembly itself (name,version,required external assemblies etc.)


In addition to manifest data, an assembly contains metadata that describes the composition (member names,implemented interfaces, base classes, constructors ,etc) of every contained type.


Understanding the Format of a .NET Assembly


Structurally speaking a .NET assembly (*.dll or *.exe) consist of the following elements.


  1. An OS File Header(for e.g. windows)
  2. A CLR File Header
  3. CIL Code
  4. Type Metadata
  5. An assembly manifest
  6. Optional embedded resources.


Installing the C# Profiling Tools

Visual Studio Installer -> C++ Profiling Tools (Download because this includes dumpbin.exe)


The Operation System (Windows) File Header

The OS File Header establishes the fast that the assembly can be loaded and manipulated by the target OS. This header data also identifies the kind of application (console-based, exe-based, or *.dll code library) to be hosted by the OS.


dumpbin /header CarLibrary.dll


The CLR File Header

In a nutshell, this header defines numerous flags that enable the runtime to understand the layout of the managed file. For ex, flags exist that identify the location of the metadata and resources within the file, the version of the runtime the assembly was built against, the value of the (optional) publickey, and so forth.


dumpbin /clrheader CarLibrary.dll


CIL Code, Type Metadata, and the Assembly Manifest

An assembly contains CIL code, is a platform- and CPU-agnostic intermediate language. At runtime, the internal CIL is compiled on the fly using a just-in-time (JIT) compiler, according to platform- and CPU-specific instructions.


An assembly also contains metadata that completely described the format of the contained types, as well as the format of external types referenced by this assembly.


An assembly must also contain an associated manifest (also referred to as assembly metadata). The manifest documents each module within the assembly, establishes the version of the assembly, and documents any external assemblies referenced by the current assembly.


Optional Assembly Resources

A .NET assembly may contain any number of embedded-resources, such as application icons,image,files,sound clips, or string tables. In fact, the .NET platform supports satellite assemblies that contain nothing but localized resources. This can be useful if you want to partition your resources based on a specific culture (English,German,etc.) for the purposes of building international software.



Class Libraries vs Console Applications

Console applications have a single-entry point (either a specified Main() method or top-level statements), can interact with the console, and can be launched directly from the OS.


Class libraries, on the other hand, don't have an entry point and therefore can not be launched directly.