📓 3.0.0.22 Classes and Namespaces
We previously learned that .NET organizes its code into classes and namespaces:
- A class is a template for creating objects of a specific type. In this way, a class groups related functionality into one unit (a template or blueprint) that can be used again and again to create a specific type of object.
- A namespace is a grouping of related classes based on their importance or function.
In this lesson, we'll learn how to create our own custom classes and namespaces.
Creating and Using the Triangle
Class​
We actually already created a basic Triangle
class in the previous lesson. Let's take another look at it:
public class Triangle
{
// Code for Triangle business logic will go here.
}
To create a class, we need to do the following:
- Declare a class using the
class
keyword followed by the name of the class in Pascal case, or "UpperCamelCase". Our class is calledTriangle
. - Include the access modifier
public
in order to make our class accessible anywhere within our application. There are other access modifiers thanpublic
, and we won't always include an access modifier. We'll learn about access modifiers in depth in an upcoming lesson. - On new lines, we include an opening
{
and closing}
curly brace. It's within these curly braces that we will later add a constructor, methods, and more!
And that's all that we need to create and use a class, albeit not a very useful class.
Creating a New Triangle
Instance​
Let's try using our class in our user interface logic file, Program.cs
. We'll simply create a new instance of the Triangle
class and print the object to the console:
using System;
class Program
{
static void Main()
{
Triangle testTriangle = new Triangle();
Console.WriteLine(testTriangle.GetType());
}
}
We've update Program.cs
in a few ways:
- We've added a using directive for
System
in order to access theConsole
library so we can write to the terminal. - We've created a new instance of the Triangle class with
Triangle testTriangle = new Triangle();
. This should look similar to JavaScript classes as well as using C# lists and dictionaries:- We start by creating a variable:
Triangle testTriangle
. - We then use the assignment operator
=
to set thetestTriangle
variable to a newTriangle
object which we create using thenew
keyword and by invoking theTriangle()
constructor method.
- We start by creating a variable:
- We get and print the type of the
testTriangle
variable to the terminal usingConsole.WriteLine()
.
Notice that we do not need to create a constructor in our class in order to have access to a constructor method. That is because a constructor method is automatically created anytime you create a new class. Later, we'll learn how to customize class constructor methods.
Compiling and Executing the App​
We can compile and execute our console app by running the following command within the ShapeTracker/
project directory:
$ dotnet run
We should see the name of our class output in the terminal:
Triangle
That's not very exciting, but this does confirm that our Triangle
class is working and we've successfully created an instance of it.
Remember that the dotnet run
command both compiles and runs our project. If we only want to compile our project, we would use the command dotnet build
.
Compiler Errors​
Keep in mind that we need to include a semicolon after every statement and "using" directive within our C# code files.
For example, if we remove the semicolon from the end of Console.WriteLine(testTriangle)
in Program.cs
, we'll get a loud red error in our terminal stating the following:
/Users/staff/Desktop/ShapeTracker.Solution/ShapeTracker/Program.cs(7,36): error CS1002: ; expected [/Users/staff/Desktop/ShapeTracker.Solution/ShapeTracker/ShapeTracker.csproj] The build failed. Fix the build errors and run again.
Try it out!
This type of error is called a compiler error, which is an error generated when our code is being compiled. Compiler errors break our code so they need to be fixed before we can successfully run our apps.
Creating and Using Namespaces​
Right now we have two classes in our application:
Program
, which contains our user interface code, like theMain()
method.Triangle
, which contains business logic forTriangle
objects.
With only two classes, it may not seem like it makes sense to group these into namespaces, but it is actually a best practice to intentionally set up namespaces from the start of every program.
We'll use two namespaces in our Shape Tracker app, one for our business logic and another for our user interface (UI) logic. With these namespaces in place, if we add more user interface or business logic files later, we can group them into their appropriate namespace.
Adding a Namespace for UI Logic​
Let's add a namespace to Program.cs
, the only code file we have so far for user interface logic.
using System;
namespace ShapeTracker
{
class Program
{
static void Main()
{
Triangle testTriangle = new Triangle();
Console.WriteLine(testTriangle.GetType());
}
}
}
Creating a namespace is similar to creating a class:
- We use the
namespace
keyword to declare a namespace followed by the name of the namespace in Pascal case, or "UpperCamelCase". - On new lines, we include an opening
{
and closing}
curly brace. It's within these curly braces that we add the class(es) that we want included in the namespace.
As far as conventions go:
- It's common to name all namespaces in a project starting with the name of the project or company.
- It's common for the entry point of the application to have a namespace name that is just the name of the project or company.
- When .NET compiles, it locates the entry point of our application by the method name
Main()
and the fact that it is astatic
method. So we don't have to worry about our namespace or class name affecting the compiler's ability to locate theMain()
method.
Adding a Namespace for Business Logic​
Next, let's create a namespace for our business logic file, Triangle.cs
:
namespace ShapeTracker.Models
{
public class Triangle
{
}
}
We're naming this namespace ShapeTracker.Models
because it will hold all of the classes that contain our business logic. Why not ShapeTracker.BusinessLogic
? The name models is the standard name in C# to describe "business logic".
We're seeing new conventions with the namespace name ShapeTracker.Models
, so let's review those now:
- We can give a namespace multiple names, separated by a period
.
. In this case we're using two names, the name of our project,ShapeTracker
, and the name of a component within our project,Models
, to create one namespace:ShapeTracker.Models
. - A component is one aspect of our project. Our two components are user interface and business logic, two basic groupings we can organize our code with; however, we could identify components in other ways.
In summary, with the namespace ShapeTracker.Models
, we're seeing another very common convention in namespace naming: start with the name of the project or company and then add the name of the component or feature that the namespace groups.
To read more about naming namespaces, visit the MS docs.
We Access Classes through their Namespace​
Let's re-run our console app now that we have our two new namespaces in place. Run the following command within the ShapeTracker/
project directory:
$ dotnet run
Uh-oh... we're getting compiler errors!
/Users/staff/Desktop/ShapeTracker.Solution/ShapeTracker/Program.cs(10,7): error CS0246: The type or namespace name 'Triangle' could not be found (are you missing a using directive or an assembly reference?) [/Users/staff/Desktop/ShapeTracker.Solution/ShapeTracker/test.csproj] /Users/staff/Desktop/ShapeTracker.Solution/ShapeTracker/Program.cs(10,35): error CS0246: The type or namespace name 'Triangle' could not be found (are you missing a using directive or an assembly reference?) [/Users/staff/Desktop/ShapeTracker.Solution/ShapeTracker/test.csproj] The build failed. Fix the build errors and run again.
We're actually getting the same error twice: once for each time we reference the Triangle
type in Program.cs
:
Triangle testTriangle = new Triangle();
What this error message is telling us is that the compiler cannot locate our Triangle
class. Can you guess why this might be?
It's because the Triangle
class doesn't exist anymore. Instead we have a ShapeTracker.Models.Triangle
class. As we can see, when we add a class to a namespace, the name of our class is updated to include the namespace.
Let's fix the compiler errors. Here's how we'll update Program.cs
:
using System;
namespace ShapeTracker
{
class Program
{
static void Main()
{
ShapeTracker.Models.Triangle testTriangle = new ShapeTracker.Models.Triangle();
Console.WriteLine(testTriangle.GetType());
}
}
}
Now if we run our app, our compiler errors will be resolved. We'll see that even the value of testTriangle
has updated to show both its namespace and class name:
ShapeTracker.Models.Triangle
Typing out ShapeTracker.Models.Triangle
every time we need to reference the Triangle
class is going to get tedious fast, so let's update Program.cs
with a new using directive for ShapeTracker.Models
:
using System;
using ShapeTracker.Models; // new directive
namespace ShapeTracker
{
class Program
{
static void Main()
{
Triangle testTriangle = new Triangle(); // updated code
Console.WriteLine(testTriangle.GetType());
}
}
}
That's better. Now if we run our app, we'll still get the same value as before: ShapeTracker.Models.Triangle
.
Takeaways​
Namespaces and classes allow us to group and organize our code.
It's important to set up namespaces from the start of your application, even if you won't have that many files and classes. This ensures that we are being intentional with our code organization and writing code that can scale easily. In the example projects we create, we will create a namespace for UI logic and another for business logic.
Namespaces and classes are declared in a similar way, using a namespace
or class
keyword. Classes are always nested inside of namespaces.
Once we add a class to a namespace, we need to access that class via the namespace. This is just like we saw with the Triangle
class after we added it to the ShapeTracker.Models
namespace: we needed to access ShapeTracker.Models.Triangle
instead of just Triangle
.
Now that we have two namespaces declared in our Shape Tracker app, we can do more with them:
- We can add more classes to each namespace, either in the same file or in another file. Whether we do this depends on our project. For example, if we added a
Rectangle
class to our Shape Tracker app, then we'd create a newRectangle.cs
file, with a newRectangle
class inside of theShapeTracker.Models
namespace. We'll see what this looks like later on. - We can also create namespaces within this namespace, but we won't ever get into that sort of nesting in the projects we create in the program.
- If two classes are in the same namespace, they will 'know' about each other and be in the same scope.
Repository Reference​
Follow the link below to view how a sample version of the project should look at this point. Note that this is a link to a the default branch in the repository.
Example GitHub Repo for Shape Tracker: 1_basic_console_app_structure