Operator Overloading
Overview
Section titled “Overview”In Chapter 2, you learned how to compose Specifications using And(), Or(), Not() methods. In this chapter, you will learn how to use C# operator overloading to achieve more concise and intuitive syntax with the &, |, ! operators.
Instead of
inStock.And(affordable.And(electronics.Not())), you can writeinStock & affordable & !electronics.
Learning Objectives
Section titled “Learning Objectives”Key Learning Objectives
Section titled “Key Learning Objectives”-
Understanding operator overloading syntax
&=And(),|=Or(),!=Not()- Confirming that methods and operators produce identical results
-
Understanding why these operators were chosen
- Why
&/|are used instead of&&/|| - C# operator overloading constraints
- Why
-
Comparing readability and expressiveness
- Pros and cons of method chaining vs operator syntax
What You Will Verify Through Practice
Section titled “What You Will Verify Through Practice”- Comparing results between method and operator approaches
- Operator expressions for complex conditions
Key Concepts
Section titled “Key Concepts”Operator Overloading Implementation
Section titled “Operator Overloading Implementation”The Specification<T> base class overloads three operators:
public static Specification<T> operator &(Specification<T> left, Specification<T> right) => left.IsAll ? right : right.IsAll ? left : new AndSpecification<T>(left, right);
public static Specification<T> operator |(Specification<T> left, Specification<T> right) => new OrSpecification<T>(left, right);
public static Specification<T> operator !(Specification<T> spec) => new NotSpecification<T>(spec);Why &/|? (Why not &&/||)
Section titled “Why &/|? (Why not &&/||)”In C#, && and || cannot be directly overloaded. && is automatically generated by the compiler by combining & with true/false operators, but since a Specification is not bool, defining true/false operators would be semantically incorrect.
& and | are bitwise operators, but in the Specification context, they read naturally as logical AND/OR.
Identity Element Optimization in the & Operator
Section titled “Identity Element Optimization in the & Operator”Unlike the And() method, the & operator includes All identity element optimization:
// & operator: Returns the other operand as-is if Allleft.IsAll ? right : right.IsAll ? left : new AndSpecification<T>(left, right)
// And() method: Always creates a new AndSpecificationnew AndSpecification<T>(this, other)This optimization is covered in detail in Chapter 4: All Identity Element.
Project Description
Section titled “Project Description”Project Structure
Section titled “Project Structure”Operators/├── Program.cs├── Product.cs├── Specifications/│ ├── ProductInStockSpec.cs│ ├── ProductPriceRangeSpec.cs│ └── ProductCategorySpec.cs└── Operators.csproj
Operators.Tests.Unit/├── OperatorTests.cs├── Using.cs├── xunit.runner.json└── Operators.Tests.Unit.csprojCore Code
Section titled “Core Code”Method vs Operator Comparison
Section titled “Method vs Operator Comparison”// Method approachvar method = inStock.And(affordable).And(electronics.Not());
// Operator approach (identical result)var op = inStock & affordable & !electronics;Summary at a Glance
Section titled “Summary at a Glance”The following table shows the mapping between operators and methods.
Operator Mapping
Section titled “Operator Mapping”| Operator | Method | Meaning |
|---|---|---|
& | And() | Both conditions satisfied |
| | Or() | At least one satisfied |
! | Not() | Condition inverted |
The following table compares the differences between the method and operator approaches.
Method vs Operator
Section titled “Method vs Operator”| Aspect | Method Approach | Operator Approach |
|---|---|---|
| Readability | Explicit | Concise |
| Chaining | .And().And() | & & |
| All Optimization | None | Included in & operator |
| Result | Identical | Identical |
Q1: Should I use methods or operators?
Section titled “Q1: Should I use methods or operators?”A: It depends on team conventions. Operators are concise, but for developers unfamiliar with the Specification pattern, And()/Or() methods may be clearer. Both approaches produce identical results, so choose consistently.
Q2: Can you explain in more detail why &&/|| cannot be used?
Section titled “Q2: Can you explain in more detail why &&/|| cannot be used?”A: In C#, && is a combination of & + true/false operators. If true/false operators were defined on Specification<T>, code like if (spec) would become possible, but since a Specification is a condition on entities and not inherently true/false itself, this would be semantically inappropriate.
Q3: Doesn’t operator precedence cause issues?
Section titled “Q3: Doesn’t operator precedence cause issues?”A: In C#, ! has higher precedence than &, and & has higher precedence than |. Therefore, inStock & !electronics | affordable is evaluated as (inStock & (!electronics)) | affordable. For complex expressions, using parentheses to clarify intent is recommended.
Q4: What exactly is the difference between the & operator and the And() method?
Section titled “Q4: What exactly is the difference between the & operator and the And() method?”A: The results are nearly identical, but the & operator includes All identity element optimization. All & X returns X as-is, and X & All also returns X as-is. The And() method always creates a new AndSpecification. This difference is covered in detail in Chapter 4.
In the next chapter, we introduce the Specification<T>.All identity element. This special Specification serves as the starting point for dynamic filter chaining.