The .NET Common Language Specification (CLS) defines the five types of C# types used by .NET programming languages. They include class, structure, interface, enumeration, and delegate. These types represent the most common programming constructs used for writing code in .NET.
Some languages include additional types to support their needs or because they are inherited from past frameworks. For example, Visual Basic .NET (VB.NET) also includes the Module type since its introduction in 1991. It behaves just like a C# static class. F# includes many mathematical constructs such as tuples.
The key skill for software designers and developers is to know when to use each of these types and to use them wisely. By using the correct types to represent the functionality we need, we improve code maintainability, memory and processor efficiency, and increase application performance.
The following list is for C# developers, although VB developers can also use this list. The key thing to remember is that each of these C# types should always be defined as “internal” (VB uses “Friend”) unless the type needs to be exposed outside the assembly which requires “public”. Note that if not specified, the types defaults to “internal” anyway. Those are the only two visibility levels allowed unless you are nesting it inside another type. Here we go!
This is by far the most common type we use when writing code. A class represents a thing, a noun – tangible or intangible. It is a container that holds variables (often called fields) and functionality with methods. Most of the code written by C#, Java, C++ developers is placed inside of classes.
Many times, I quiz my students by asking them to identify all the members that can be defined inside of a class. The final list usually includes the following items: Field, Property, Method, Attribute, Event, Instance Constructor, Static Constructor, Finalizer, Nested Type. I hesitate to include the attribute because it cannot be defined on its own. It must be defined immediately before (above) a type or type member, altering its behavior.
I consider the structure (C#’s struct) to be the most under-utilized type. It is significantly much lighter weight than a class yet most developers just define classes for every container type they need.
The struct type contains its own data and lives entirely on the stack. Among other purposes, it can be used to pass a bunch of related values into a method “by value” as a single parameter rather than passing them in as several parameters. As well, when you assign one struct instance to another variable, the struct’s values are copied to the new object rather than just copying a memory address like class instances do.
Like a class, it too can represent a type and can contain the same members such as fields, method, properties, etc. Although we cannot override its default constructor and it cannot support inheritance, it has other benefits as well!
By not supporting inheritance, there are less constructors that need to run when instantiated, meaning they are faster to create in memory. As well, they live entirely on the stack – not on the heap. When they fall out of scope, they are immediately removed from the stack – no garbage collection required!
One caveat – if you define a struct that implements an interface (covered next), it is immediately boxed on the heap as a standard object that must eventually be garbage collected, losing all advantages of being a struct. You may as well use a class instead.
There are two primary ways polymorphism is supported in .NET – overridden methods in derived classes and implemented interfaces. Polymorphism is based on the Greek word “morph“, which means to form, to change. Polymorphism is primarily used with the methods of a class. You can morph (change) a method in a derived class or in class that implements an interface member.
Interfaces allow developers to define a pattern that must exist in classes that are otherwise completely unrelated to each other. It can be a way of grouping types together that would otherwise be impossible to correlate. An interface defines a contract, a set of rules that must be followed by the implementing class or the code simply will not compile.
Interfaces members cannot contain any real code nor can their visibility be set. Interface members include methods, properties, events, and indexers. Interfaces are said to be immutable, meaning they can never change once they have been implemented. The only way to add members to an interface is to define a new one that inherits from it. Interfaces can inherit from multiple interfaces while classes can implement multiple interfaces.
Interfaces can be implemented explicitly or implicitly. Both patterns require a class to include all interface members defined as class members. Explicit interface implementation requires that all members are defined implicitly private and are only accessible by casting the object as the interface. Implicit implementation requires the interface members be defined as regular class members. Strangely, implicit interface implementation requires all implemented members be defined as public, even if the interface and the class implementing it are defined as internal.
As the name suggests, enumerations (C#’s enum) allow you to assign integers to a bunch of related string values. Once defined, variables can be create based on these strings. At our discretion, when working with an enum variable, we can either retrieve the integer value or the string value.
Enums also help us control what is assigned to a variable, property, or passed into a method. This helps us protect our code while guiding the developer to use the correct values with our members. For example, if a method expects a day of the week to be pass into it, it’s best to use an enum parameter so that the code calling it can only pass in one of the seven possible values defined by the enum. If instead the method accepts a string, the developer could pass in anything (Friday, F, Fri, Funday) – possibly breaking our code.
The delegate type typically incurs the greatest learning curve for developers. At its foundation, it can be used much like a C++ function pointer. However, it can do much more than that in C# and .NET. The four basic uses of a delegate include single function pointer, multiple function pointer, the entire foundation for events, and a way of implementing the Asynchronous Programming Model (APM).
The most prevalent way C# developers use delegate is with events. For C# developers, to truly understand events in .NET, they must understand delegates. Every event in .NET must be based on a delegate. The delegate is used to define the method signature of the event handlers that can be assigned to the event.
Developers can define their own delegate or use one included with .NET. The most used delegate included with .NET is EventHandler. It is responsible for the many events in .NET classes and controls being defined with the two parameters, Object and EventArgs.
I hope you have found this list of the five types of C# types helpful. Often times their capabilities can be overlooked and may not run as efficiently as needed. It’s up to the software designers and developers to make sure the best type is chosen to represent the structure needed. Ciao for no