Namespaces and Assemblies

Namespaces organize types and prevent name clashes: SampleApp.Diagnostics.Hardware.ElectronicControlUnit.

Concept

Namespaces organize types and prevent name clashes: SampleApp.Diagnostics.Hardware.ElectronicControlUnit.

namespace A.B {... } File-scoped namespace (C# 10) namespace A.B; — applies to the whole file, saves an indent level A.B.C (dots) or nested blocks using directive

using System.Text; — use types without the namespace prefix Global using (C# 10)

global using System.Text; — applies to the whole project; implicit usings add common ones automatically

using Ecu = SampleApp.Diagnostics.Hardware.ElectronicControlUnit;

using static System.Math; — call Sqrt directly Namespaces are logical; they are not tied to assemblies (one assembly can contain many namespaces, and one namespace can span assemblies).

An assembly is the unit of deployment, versioning and security: a .dll (library) or .exe host plus .dll (application) containing IL, metadata and a manifest (name, version, culture, public key token, references).

References: projects reference other projects or NuGet packages; the compiler records assembly references in metadata.

Strong naming (public key + signature) gives an assembly a unique identity; it matters mostly for .NET Framework and some enterprise scenarios; modern .NET does not require it.

DLL vs EXE: in .NET, applications compile to a .dll; the .exe is a small native apphost that loads the runtime and your .dll.

Why It Matters

Namespaces keep large codebases navigable; assemblies define deployment boundaries, versioning and internal visibility (Module 13).

Syntax

namespace Company.Product.Feature;          // file-scoped
using Alias = Some.Long.Namespace.TypeName;
global using System.Text.Json;
using static System.Console;

Example

// Global usings would normally live in one file: global using System.Text;
using System.Reflection;
using Ecu = SampleApp.Diagnostics.Hardware.ElectronicControlUnit;   // alias
using static System.Math;                                               // static import

var ecu = new Ecu("VCU");
Console.WriteLine($"{ecu.GetType().FullName}");
Console.WriteLine($"namespace: {typeof(Ecu).Namespace}");
Console.WriteLine($"Sqrt(2) via using static: {Sqrt(2):F4}");

// Assemblies: the unit of deployment and versioning
Assembly self = typeof(Ecu).Assembly;
Assembly core = typeof(object).Assembly;
Assembly linq = typeof(Enumerable).Assembly;
Console.WriteLine($"this program's assembly: {self.GetName().Name}, entry point: {self.EntryPoint?.Name}");
Console.WriteLine($"object lives in {core.GetName().Name} v{core.GetName().Version}");
Console.WriteLine($"Enumerable lives in {linq.GetName().Name}");
Console.WriteLine($"referenced assemblies: {string.Join(", ", self.GetReferencedAssemblies().Select(a => a.Name).Where(n => n!.StartsWith("System")).Order().Take(4))} ...");
Console.WriteLine($"public key token of System.Private.CoreLib: {Convert.ToHexString(core.GetName().GetPublicKeyToken() ?? [])
…

Expected Result

The top-level statements compile into a generated entry point named <Main>$. Core types such as object live in System.Private.CoreLib, which carries the well-known public key token 7cec85d7bea7798e.

SampleApp.Diagnostics.Hardware.ElectronicControlUnit
namespace: SampleApp.Diagnostics.Hardware
Sqrt(2) via using static: 1.4142
this program's assembly: m23_namespaces, entry point: <Main>$
object lives in System.Private.CoreLib v10.0.0.0
Enumerable lives in System.Linq
referenced assemblies: System.Console, System.Linq, System.Runtime ...
public key token of System.Private.CoreLib: 7CEC85D7BEA7798E

Common Mistakes

Namespace names that do not match the folder structure (hard to navigate).

Two types with the same name in different imported namespaces (error CS0104 "ambiguous reference") — use an alias or full name.

Putting too many global usings in a project, hiding where types come from.

Circular project references (not allowed) — extract shared code into a third project.

Best Practices

Company.Product.Area naming, matching folders; one file-scoped namespace per file.

Keep global usings in one GlobalUsings.cs file.

Use internal for implementation details and InternalsVisibleTo for test projects.

Split assemblies by deployment or dependency boundaries, not by type count.

Interview Relevance

Medium: "namespace vs assembly", "what is in an assembly manifest?", "what is a strong name?", "what does internal mean?", "file-scoped namespaces".

Practice Questions

Convert a block-namespace file to a file-scoped namespace.

Create an alias for System.Collections.Generic.Dictionary<string, List<int>>.

Print the version and location of the assembly that contains System.Text.Json.JsonSerializer.

Create two projects where one uses an internal class of the other via InternalsVisibleTo.

Quick Quiz

Q1. (True/False) One assembly can contain several namespaces.

Answer: True. Q2. (Multiple choice) What is the name of the method generated for top-level statements? A) Main B) <Main>$ C) Start D) Program Answer: B (verified).

Q3. (Multiple choice) Which directive lets you call Sqrt(2) without Math.? A) using System.Math; B) using static System.Math; C) global using Math; D) import Math; Answer: B.

Q4. (Find the bug) Two imported namespaces both define Timer; new Timer() fails to compile. Fix?

Answer: Use the full name or an alias: using Timer = System.Threading.Timer;.

Coding Challenge

Print the name and version of the assemblies that define List<T>, Regex, HttpClient and JsonSerializer.

Type[] types = [typeof(List<int>), typeof(System.Text.RegularExpressions.Regex),
                typeof(System.Net.Http.HttpClient), typeof(System.Text.Json.JsonSerializer)];
foreach (Type t in types)
{
    var name = t.Assembly.GetName();
    Console.WriteLine($"{t.Name,-16} {name.Name} {name.Version}");
}