What are partial classes and partial methods in C#?
Quick Answer
Partial classes allow splitting a class definition across multiple files. Partial methods allow declaring a method in one part and implementing it in another (optional implementation). Useful for code generation, separating generated code from custom code, and organizing large classes. Partial methods must be void and cannot have access modifiers.
Detailed Answer
Partial classes allow you to split a single class definition across multiple files, while partial methods allow you to declare a method in one part and optionally implement it in another part. This is particularly useful for code generation, designer files, and organizing large classes.
Partial Classes:
Benefits:
- Split large classes across multiple files
- Separate generated code from hand-written code
- Organize related functionality
- Enable multiple developers to work on the same class
Example:
// File: Person.cs
public partial class Person
{
private string firstName;
private string lastName;
public Person(string firstName, string lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}
public string GetFullName()
{
return $"{firstName} {lastName}";
}
}
// File: Person.Properties.cs
public partial class Person
{
public string FirstName
{
get => firstName;
set => firstName = value ?? throw new ArgumentNullException(nameof(value));
}
public string LastName
{
get => lastName;
set => lastName = value ?? throw new ArgumentNullException(nameof(value));
}
public int Age { get; set; }
public string Email { get; set; }
}
// File: Person.Methods.cs
public partial class Person
{
public void DisplayInfo()
{
Console.WriteLine($"Name: {GetFullName()}");
Console.WriteLine($"Age: {Age}");
Console.WriteLine($"Email: {Email}");
}
public bool IsAdult()
{
return Age >= 18;
}
public void SendEmail(string subject, string body)
{
if (string.IsNullOrEmpty(Email))
throw new InvalidOperationException("Email address is not set");
Console.WriteLine($"Sending email to {Email}");
Console.WriteLine($"Subject: {subject}");
Console.WriteLine($"Body: {body}");
}
}
// File: Person.Validation.cs
public partial class Person
{
public bool Validate()
{
return !string.IsNullOrEmpty(firstName) &&
!string.IsNullOrEmpty(lastName) &&
Age >= 0 &&
IsValidEmail(Email);
}
private bool IsValidEmail(string email)
{
return !string.IsNullOrEmpty(email) && email.Contains("@");
}
}
Advanced Example - Code Generation Scenario:
// File: User.cs (Hand-written code)
public partial class User
{
private int id;
private string username;
private string email;
public User(string username, string email)
{
this.username = username;
this.email = email;
}
// Hand-written business logic
public bool IsActive()
{
return !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(email);
}
public void UpdateProfile(string newEmail)
{
if (IsValidEmail(newEmail))
{
email = newEmail;
OnProfileUpdated(); // Partial method call
}
}
// Partial method declaration - implemented in generated code
partial void OnProfileUpdated();
// Partial method for validation - implemented in generated code
partial void ValidateUser();
private bool IsValidEmail(string email)
{
return !string.IsNullOrEmpty(email) && email.Contains("@");
}
}
// File: User.Generated.cs (Generated code - e.g., from Entity Framework)
public partial class User
{
// Generated properties
public int Id
{
get => id;
set => id = value;
}
public string Username
{
get => username;
set => username = value;
}
public string Email
{
get => email;
set => email = value;
}
// Generated methods
public override string ToString()
{
return $"User: {username} ({email})";
}
public override bool Equals(object obj)
{
if (obj is User other)
return id == other.id;
return false;
}
public override int GetHashCode()
{
return id.GetHashCode();
}
// Partial method implementations
partial void OnProfileUpdated()
{
Console.WriteLine($"Profile updated for user: {username}");
// Could trigger events, update database, etc.
}
partial void ValidateUser()
{
if (string.IsNullOrEmpty(username))
throw new InvalidOperationException("Username is required");
if (string.IsNullOrEmpty(email))
throw new InvalidOperationException("Email is required");
}
}
Partial Methods:
Key Characteristics:
- Must be declared with
partialkeyword - Must return
void - Cannot have access modifiers (implicitly
private) - Cannot be
virtual,override,sealed, orextern - Can have
refandoutparameters - If not implemented, the compiler removes the method call
Example:
public partial class DataProcessor
{
public void ProcessData(string data)
{
Console.WriteLine("Processing data...");
// Partial method call - only executed if implemented
OnDataProcessingStarted(data);
// Processing logic here
var result = data.ToUpper();
// Partial method call - only executed if implemented
OnDataProcessingCompleted(result);
Console.WriteLine($"Result: {result}");
}
// Partial method declarations
partial void OnDataProcessingStarted(string data);
partial void OnDataProcessingCompleted(string result);
}
// In another file, you can optionally implement the partial methods
public partial class DataProcessor
{
partial void OnDataProcessingStarted(string data)
{
Console.WriteLine($"Starting to process: {data}");
}
partial void OnDataProcessingCompleted(string result)
{
Console.WriteLine($"Processing completed: {result}");
}
}
Use Cases:
Partial Classes:
- Code generation (Entity Framework, WPF designers)
- Large classes that need organization
- Separating generated code from custom code
- Team development on the same class
Partial Methods:
- Code generation scenarios
- Optional customization points
- Performance optimization (unused methods are removed)
- Template method pattern implementation
Best Practices:
- Use partial classes to organize large classes logically
- Keep related functionality together in the same partial class
- Use partial methods for optional customization points
- Document when partial methods are expected to be implemented
- Be careful with partial classes in inheritance hierarchies