Today’s post attempts to answer a very simple and straightforward question: “When is it OK to use a C# partial class?” And the answer as straightforward as this: “When you need to allow the user to edit code that was automatically generated.”
Whoa, that was hands down the easiest post I’ve ever written. So, that’s it for today, folks. Thanks for reading and see you next time!
OK, I’m just kidding, of course. If you’ve found this page by googling around on “C# partial class,” chances are you have a number of questions regarding this topic. For starters, what is a partial class? How do you actually use it? Are there any restrictions regarding its use? And the list could go on.
That’s what today’s post is all about. We’ll start out by explaining what a partial class is and providing some examples. Then, we’ll go a little bit deeper, giving a more detailed view of the partial class, its inner workings, and the rules that must be followed when using it.
Finally, we’ll explain the main use case for the C# partial class, nicely tying the end of the post to its beginning.
What Is a Partial Class?
You’d probably agree with me that it doesn’t make a lot of sense to talk about the appropriate uses for partial classes without first understanding what they are, right? So, let’s do that.
Well, for starters, although the term we’ve been using is “C# partial class,” the functionality itself isn’t restricted to classes only. Instead, it equally applies to structs, interfaces, and methods. And what is this “functionality”? It’s the possibility of splitting up the definition of a method, interface, struct, or class into more than one source code file.
In practice, that’s working by using the “partial” keyword. Consider the following excerpt:
1 2 3 4 |
public partial class Person { public string Name { get; private set; } } |
Now, we could have another file containing the code for another property:
1 2 3 4 |
public partial class Person { public DateTime BirthDate { get; private set; } } |
Let’s add a third file, which will house the code for a method called “Introduce”:
1 2 3 4 |
public string Introduce() { return $"Hi, my name is {Name} and I'm {CalculateAge()} years old."; } |
As you can see, the “Introduce” returns a string with some data about the person. In order to do so, it calls the “CalculateAge” method. And where’s the code for this method? Yep, you’ve guessed it: in yet another file:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public partial class Person { private int CalculateAge() { var today = DateTime.Today; var years = today.Year - BirthDate.Year; if (BirthDate.AddYears(years).Date > today) years--; return years; } } |
Finally, let’s add one last file. It will contain our class’s constructor, on which we’ll initialize the properties of the class. That’s necessary since our properties have private setters:
1 2 3 4 5 6 7 8 |
public partial class Person { public Person(string name, DateTime birthDate) { Name = name; BirthDate = birthDate; } } |
It’d be probably wise to add a null check before initializing the “Name” property. Well, at least until C# 8.0 is a reality. In the example, I’ve omitted the check for the sake of brevity.
With all those definitions in place, we’re now ready to use the class as we would use any other class. The following unit tests pass:
1 2 3 4 5 6 7 8 9 |
[Test] public void Introduce() { var person = new Person("John Doe", new DateTime(1992, 2, 29)); Assert.AreEqual("Hi, my name is John Doe and I'm 26 years old.", person.Introduce()); Assert.AreEqual("John Doe", person.Name); Assert.AreEqual(new DateTime(1992, 2, 29), person.BirthDate); } |
As you’ve seen, we can easily split a class’ definition into multiple files by using the “partial” keyword. The consumer code will see it as a single, regular class and use it as such.
So, is that all there is to it? Of course not. In the next section, we’ll give you a more detailed view on this C# construct, covering some of the restrictions that you must be aware of when using it.
Going Further With the C# Partial Class: Meet Some Restrictions
From the previous section, you might’ve gotten the impression that it’s very easy to declare and use a C# partial class (or struct/interface). It is easy indeed, but that doesn’t mean that aren’t rules and restrictions you must follow when using this functionality.
On the Use of the Partial Keyword
For starters, all components of the partial type must have the partial keyword. Let’s remove the keyword from one of the declarations in our examples:
1 2 3 4 5 6 7 |
public class Person { public string Introduce() { return $"Hi, my name is {Name} and I'm {CalculateAge()} years old."; } } |
The compiler promptly lets us know we’re doing something wrong. Here’s the error message we get:
Missing partial modifier on declaration of type ‘Person’; another partial declaration of this type exists
Compiling error messages are sometimes confusing and not very helpful, but this one is spot on. Adding the partial keyword again makes the compiler happy, and the message goes away.
Restrictions About Accessibility
The next restriction has to do with accessibility. All components of a partial type must have the same accessibility level. Let’s violate this rule by changing the first file from our example:
1 2 3 4 |
private partial class Person { public string Name { get; private set; } } |
The error message we get this time is as helpful and declarative as the previous one:
1 |
Partial declarations of 'Person' have conflicting accessibility modifiers |
Reverting back the file is enough to make the error go away.
There are more restrictions a partial type should follow. All of the component parts of the type must reside in the same assembly, i.e. the .exe or .dll file resulting from the build.
Restrictions on the Partial Keyword
There’s yet another restriction: you can’t just use the partial keyword wherever you feel like it. It’s only allowed before the class, struct, and interface keywords.
The keywords abstract, sealed, private, protected, internal, and public are optional when using a partial type. But if you do use them, then they must not conflict with the keywords used in the other part of the type. Let’s see an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// file 'book.cs' public abstract partial class Book { public int Id { get; set; } public string Title { get; set; } public string Isbn { get; set; } } // file 'book1.cs' public sealed partial class Book { } |
The problem here is that the class is being defined as both abstract and sealed, which doesn’t many any sense since these things are the perfect opposite of one another. Always remember this: a partial class is not the same as many classes with the same name. Rather, it’s one class split up into several files.
Our friend the compiler comes to our rescue again. This is the message we get:
‘Book’: an abstract class cannot be sealed or static
What to do to get rid of the message? Just choose one of the keywords to keep and remove the other from the declaration. For instance:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// file 'book.cs' public partial class Book { public int Id { get; set; } public string Title { get; set; } public string Isbn { get; set; } } // file 'book1.cs' public sealed partial class Book { } |
In the code above, we’ve deleted the “abstract” keyword from the first partial declaration, keeping only the “sealed” on the second file. That way, the whole class will be considered sealed, even if we add more partial declarations to it in the future.
C# Partial Class: the Use Case
With all that in mind, we’re ready to tackle the main question in this post. As we answered right at the beginning of the post, the main use case for the partial class is to allow developers to extend automatically generated code.
But why is that? Why you even need to do this?
In short, being able to edit automatically generated classes is somewhat tricky. Any code that you add to a generated class might be potentially overwritten when the class is generated again—not to mention the fact that you might accidentally mess with some piece of the code you weren’t supposed to and get into trouble.
The way developers solved this problem before partial classes were suboptimal. They’d divide the class into areas, often using the “region” construct. Then they would just edit the area of the class that was clearly marked as so. Likewise, when automatic generation kicked in, it was supposed to only replace the area defined for that.
I’m sure you can already see the problems of such an approach. It was way too easy for a developer to add code to the wrong area and thus lose their work when code generation ran. Or worse, they could cause code generation to not work properly, or even not work at all, by editing code that shouldn’t be manually altered.
Partial classes remove the pain from those scenarios. By having the same type split into different files, you can easily prevent developers from editing the wrong files. In other words, you can prevent bugs and loss of productivity.
Back to You
In today’s post, you’ve read about the C# partial class: what it is, how it works, and why you’d want to use it. It’s an interesting C# feature that has a very narrow use case. Unlike, let’s say, LINQ, this isn’t really a feature that you, as a C# developer, are going to use every day.
But when you find yourself implementing some code generation feature, you’ll be glad that this feature exists. Hopefully, you were also glad to learn a little bit more about it by reading this post. Thanks for the read and see you next time!
Thanks for this, I enjoyed reading it.
I have in the past come across another use case where a partial class was exactly what was needed. There was a particular application class that was undergoing two streams of work. One team of developers was adding new functionality, whilst another team were performing heavy refactoring to the existing code.
Without careful management there was likely to be complex merge issues. The use of a partial class in this case was an elegantly simple solution. By temporarily splitting the class into two partial class files both teams were able to work fairly independently. Once the first team had finished the two files were recombined.
Nothing ground-breaking. But to me it was a good use case for partial classes.
Hey Tristan! Thanks for sharing your experience. That is indeed a very interesting use case for partial classes.
Are you intending on a followup blog post on partial methods?
These are where I’ve found partial classes to shine – allowing a developer to extend generated code in a controlled fashion.
I have been using partial classes as Tristan stated and it works great and i have never experienced an issue with this. Thanks for you comment Tristan. And Carlos for this post.
Ed