Skip to content

Operator Overloading

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 write inStock & affordable & !electronics.

  1. Understanding operator overloading syntax

    • & = And(), | = Or(), ! = Not()
    • Confirming that methods and operators produce identical results
  2. Understanding why these operators were chosen

    • Why &/| are used instead of &&/||
    • C# operator overloading constraints
  3. Comparing readability and expressiveness

    • Pros and cons of method chaining vs operator syntax
  • Comparing results between method and operator approaches
  • Operator expressions for complex conditions

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);

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 All
left.IsAll ? right : right.IsAll ? left : new AndSpecification<T>(left, right)
// And() method: Always creates a new AndSpecification
new AndSpecification<T>(this, other)

This optimization is covered in detail in Chapter 4: All Identity Element.

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.csproj
// Method approach
var method = inStock.And(affordable).And(electronics.Not());
// Operator approach (identical result)
var op = inStock & affordable & !electronics;

The following table shows the mapping between operators and methods.

OperatorMethodMeaning
&And()Both conditions satisfied
|Or()At least one satisfied
!Not()Condition inverted

The following table compares the differences between the method and operator approaches.

AspectMethod ApproachOperator Approach
ReadabilityExplicitConcise
Chaining.And().And()& &
All OptimizationNoneIncluded in & operator
ResultIdenticalIdentical

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.

-> Chapter 4: All Identity Element