In this article, we will review the concept of partial classes in C#.
The main idea of partial classes in C# is that the compiler constructs a single class from separate source files. It knows to combine them into a single text file before performing the language compilation. After the compilation is complete, it generates the assembly code, for example, a .dll
or an .exe
file.
This compiler capability allows multiple people or systems to work together on a single complex class. They write their code into separate files, independently or in parallel.
So, let’s create a starting point to understand and learn about this remarkable language feature.
How to Create Partial Classes in C#
To create a partial class, we start by introducing the partial
keyword as part of the class declaration, let’s say, in the Robot.cs
file:
public partial class Robot { // Class members }
And then we can add a second file, let’s call it RobotNewAbility.cs
, adding more content to the same class:
public partial class Robot { // Additional class members }
Key Restrictions of Partial Classes in C#
Now, we will review the restrictions of implementing a partial class with multiple source files.
All parts, source files, must be available for the compiler during compile time to create the assembly, be it .dll or .exe.
We can view it as if the compiler combines the source files’ text as the initial step. It does it as a preprocessing step. And only then performs the compilation itself.
All parts must share the same accessibility level, whether public
, private
, protected
, or others.
It is easy to see that compiler cannot resolve a dilemma: “whom to trust.” All decisions that it must make should be straightforward.
If the class inherits a base class, then all its parts must agree on the same base class.
If the compiler encounters a conflict in the inheritance declarations, it will fail with an error.
Each part can have its separate list of interfaces that it implements within that part’s code.
For the final list of interfaces, the compiler will construct it by merging the parts’ lists into one.
Partial Class Example: “My Pet Robot”
Let’s present a fun example of developing a partial class as a team of C# developers. For instance, each developer can be strong in something like algorithms. At the same time, others know better how to add diagnostics or observability capabilities.
Here is the task: as a team, we must create a robot. It should be able to perform different moves. The only restriction is its level of charge.
To start with the task, we will develop a C# Console Application and add a new,Robot.cs
source file:
public partial class Robot { private int _chargeRemaining; public int DistanceCovered { get; private set; } public void Recharge() { _chargeRemaining = 100; } }
In this first part of the partial class, we introduce:
public DistanceCovered
property, in which we accumulate the distance that the robot has covered so farprivate _chargeRemaining
field that helps track the charge level available to the robot to perform different movespublic Recharge
method the clients use to charge the robot to its full capacity externally
Let’s now add more features and abilities to our robot.
New Capabilities
In the following few sections, we will notice how different team members develop additional capabilities for the robot. Each will do it in a separate source file within the same project.
Capability: Walking
Here we have Marry develop the robot’s first moving capability – walking. She does it in a separate source file. She also has full access to the project sources starting from the Robot.cs
file:
namespace PartialClasses; public partial class Robot { public const int WalkChargeRequired = 1; public const int WalkDistance = 1; public void Walk() { if (_chargeRemaining < WalkChargeRequired) return; Console.WriteLine("Performing the Walk"); _chargeRemaining -= WalkChargeRequired; DistanceCovered += WalkDistance; } }
This file adds two more private fields. One assists with calculating the charge the robot needs to walk. And the other exposes the distance the robot has covered by walking.
The public method performs the walk only if there’s enough charge remaining. And if the robot indeed walked, we increased the distance it covered in the shared property.
Capability: Jumping
Let’s assume we have added a new team member, John. John brings his unique expertise in developing the jumping capability of robots. And that is what he implements for the robot here.
Also, let’s suppose that Marry has already finished her work and merged the ability to walk into the project. Now, John can utilize it in his code while working in a separate source file in the same project:
namespace PartialClasses; public partial class Robot { public const int JumpChargeRequired = 5; public const int JumpDistance = 5; public void Jump() { if (_chargeRemaining >= JumpChargeRequired) { Console.WriteLine("Performing the Jump"); _chargeRemaining -= JumpChargeRequired; DistanceCovered += JumpDistance; } else { Walk(); } } }
We notice one difference from the walking implementation. If the charge the robot needs for jumping is insufficient, it will attempt to walk instead.
Please notice how easily each team member accesses all the class members, private
, protected
, or public
to efficiently implement their tasks.
Adding Diagnostics
Finally, we will assume that our team also hires an observability master, Jessy. She will add a diagnostics routine into the same partial class:
namespace PartialClasses; public partial class Robot { public int GetChargeRemaining() { return _chargeRemaining; } public void PrintDiagnostics() { Console.WriteLine($"Distance covered: {DistanceCovered}m, charge remaining: {_chargeRemaining}%"); } }
The most noteworthy is that each source file a developer manages adds the lines of code individually. It does not “pollute” the source file with the cognitive load of fellow team members. This provides cleanliness and clarity for this rather complex class implementation.
Robot Animation
Getting to the fun part, we will take a look at an example animation of our robot from the Console Program.cs
file:
using PartialClasses; var desiredInstance = GetDesiredDistance(); int GetDesiredDistance() { try { return int.Parse(args[0]); } catch (Exception) { throw new ArgumentException("Please provide a valid integer as the only parameter"); } } Console.WriteLine($"Our robot should cover distance of {desiredInstance}m"); var myRobot = new Robot(); myRobot.Recharge(); myRobot.PrintDiagnostics(); var jump = true; while (myRobot.DistanceCovered < desiredInstance) { if (jump) { myRobot.Jump(); } else { myRobot.Walk(); } jump = !jump; myRobot.PrintDiagnostics(); }
This code first processes the distance the robot has to cover, which it receives as a parameter. The robot’s animation then follows. We request it repeatedly to jump and walk until it covers the requested distance.
Conclusion
This article does not provide a complete overview of all the use cases when we could use a partial class. Nor does it mention related features, like nested partial classes, partial methods, or partial interfaces and structures.
With that, we attempted to introduce the new realm of combined development. One where systems and developers can implement a single class while keeping the source code clean.
It also helps project managers to have an easier job of managing the development tasks of the team.