C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
4: Compiling and Running C# Applications
By now, you've probably spent some time writing code in an editor and hitting a "Run" button. It feels like magic—you click a green arrow, and your code executes. But as a professional, relying on that button is a dangerous habit. It abstracts away the actual process of how C# transforms from a text file into something a computer can execute. If you don't understand the compilation pipeline, you're going to be completely lost the first time a build fails on a CI/CD server or when you need to deploy an app to a machine that doesn't have Visual Studio installed.
The manual struggle of csc
In the early days, or if you're following a very outdated tutorial, you might see people talking about the C# Compiler (csc.exe) directly. Imagine we're building a simple DeadlineTracker—a tool that calculates how many days are left until a project is due. In the naive approach, you'd open a terminal and call the compiler manually:
csc DeadlineTracker.cs
At first, this seems fine. You get a .exe file, and you run it. But the moment you add a second file for your logic or try to use a third-party library for date formatting, this approach falls apart. You end up with a monstrous command string, manually listing every single source file and every single reference path. It's brittle, it's tedious, and it's nearly impossible to share with a teammate because your file paths are different from theirs. I've seen juniors waste hours trying to "fix" their code when the actual problem was just a missing filename in a manual compile command.
Letting the .NET CLI handle the heavy lifting
The modern, professional way to handle this is through the .NET CLI. Instead of thinking about individual files, we think about projects. When you use dotnet new console, you aren't just getting a template; you're getting a .csproj file. This is the blueprint for your application. It tells the compiler exactly which version of .NET to use and which dependencies are required.
Instead of calling csc, you use dotnet build. This command looks at that project file and orchestrates everything. It handles the dependency graph and compiles your code into an Intermediate Language (IL) assembly. When you run dotnet run, it's actually doing a build and then launching the resulting assembly through the .NET runtime. The trade-off here is a slight bit of overhead in project structure, but the payoff is massive: your build process is now codified in a file that can be checked into Git, ensuring that if it builds on my machine, it'll build on yours.
The gap between Debug and Release
One thing the "Run" button often hides is the difference between a Debug build and a Release build. When you're developing your DeadlineTracker, you're likely in Debug mode. The compiler leaves in a lot of extra metadata and skips certain optimizations so that you can set breakpoints and inspect variables. It's slower, but it's transparent.
But you would never ship a Debug build to a client. For that, you use dotnet publish -c Release. This tells the compiler to strip out the debugging fluff and aggressively optimize the IL code for performance. If you've ever wondered why your app feels sluggish during development but screams once it's deployed, this is why. The "naive" developer just copies the bin/Debug folder to the server; the professional uses a publish profile to create a clean, optimized package.
📋 Practical Task
Building and Analyzing the DeadlineTracker Binary
In this exercise, you will move away from the IDE's "Run" button to manually manage the build lifecycle of a project.
- Create a new console project named
DeadlineTracker. InsideProgram.cs, write a small program that subtracts the current date (DateTime.Now) from a hardcoded future date to print the remaining days. - Open your terminal/command prompt and navigate to the project folder.
- Run
dotnet build. Navigate into thebin/Debug/netX.X/folder (where X.X is your version) and locate theDeadlineTracker.dllfile. - Now, execute the command
dotnet publish -c Release. - Locate the
bin/Release/netX.X/publish/folder. Compare the contents of thepublishfolder with theDebugfolder. - Identify which files are present in the Release publish folder that were not in the Debug folder, and note the difference in the
.pdbfiles (symbol files) between the two environments.
There are no comments for now.