Back to Blog
C#

C# || vs |: Short-Circuiting and Bitwise OR

c# || vs |: Understand the difference between C#'s short-circuiting || and non-short-circuiting | operators, including usage, pitfalls, and performance implications.

C# operatorsshort-circuitingbitwise operationsboolean logicconditional evaluation
Diagram comparing C# logical OR and bitwise OR operators with short-circuiting behavior

c# || vs | requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

The difference between || and | in C# comes down to short-circuiting. The || operator stops evaluating as soon as the result is known, while | always evaluates both sides. This matters most when the right-hand operand has side effects or when you're working with integer bitmasks rather than booleans.

The Core Difference: Short-Circuiting vs. Full Evaluation

For boolean operands, || is the logical OR operator. It returns true if either operand is true. Crucially, it evaluates the left operand first. If the left operand is true, the entire expression is true regardless of the right operand, so the right operand is never evaluated. This is short-circuiting.

The | operator, when used with booleans, also returns true if either operand is true, but it always evaluates both operands. Even if the left operand is true, the right operand is still evaluated. This can lead to unexpected side effects if the right operand contains a method call or an assignment.

bool left = true; bool right = ThrowIfCalled(); // This method has a side effect bool result1 = left || right; // right is never called bool result2 = left | right; // right is called, side effect occurs

In this example, ThrowIfCalled() might log, modify a variable, or throw an exception. With ||, it's skipped entirely. With |, it runs. The choice between these operators often depends on whether you want guaranteed evaluation of both sides.

How Bitwise OR Works on Integers

When the operands are integers, | is a bitwise OR operator, not a logical one. It performs a bit-by-bit comparison, setting each bit in the result to 1 if either corresponding bit in the operands is 1. The || operator cannot be used with integers directly—it only accepts bool operands.

int flags = 0b0011; int mask = 0b0101; int combined = flags | mask; // result: 0b0111

This is a common pattern for combining enum flags or bitmasks. There is no short-circuiting concept here because both operands are always needed to compute the result. Using || with integers would cause a compile-time error, so the choice is not about short-circuiting but about the correct operator for the data type.

Using | with Boolean Expressions: When It Matters

Although | is primarily a bitwise operator, C# allows it with bool operands as a non-short-circuiting logical OR. This is rarely necessary, but it becomes relevant when you need to ensure that both sides are evaluated, regardless of the left side's value. For example, if you have two cleanup operations that must both run, even if the first returns true:

bool firstCleanup = CleanupTempFiles(); bool secondCleanup = CleanupCache(); bool allClean = firstCleanup | secondCleanup;

Using || here would skip CleanupCache() if CleanupTempFiles() returned true, which might leave the cache uncleaned. The | operator guarantees both methods execute. However, this pattern is often better expressed with explicit statements to improve readability:

bool firstCleanup = CleanupTempFiles(); bool secondCleanup = CleanupCache(); bool allClean = firstCleanup && secondCleanup; // still short-circuits, but both calls are already made

In practice, relying on | for side effects is rare and can confuse readers. It's usually clearer to evaluate each side separately and then combine the results.

Operator Precedence and Parentheses

Both || and | have different precedence levels. | has higher precedence than ||, which can affect how expressions are parsed. For example:

bool a = true; bool b = false; bool c = false; bool result = a || b | c;

Because | binds tighter, the expression is evaluated as a || (b | c). Since a is true, the right side is never evaluated, so b | c is ignored. If you intended (a || b) | c, you must use parentheses. This is a common source of subtle bugs when mixing both operators.

Always use parentheses when combining logical and bitwise operators to make the intended order explicit. Relying on precedence rules can lead to code that is hard to read and maintain.

Performance and Side-Effect Considerations

Short-circuiting can improve performance by avoiding unnecessary work. If the left operand of || is true, the right operand is not evaluated, saving a method call or a complex computation. This is especially beneficial in conditions like:

if (IsValid(user) || IsAdmin(user)) { ... }

If IsValid returns true, the potentially expensive IsAdmin call is skipped. With |, both methods run every time, which can add overhead. However, the performance difference is usually negligible unless the right operand is costly. The more important issue is side effects: | can cause unintended behavior if the right operand modifies state or throws an exception.

When working with integers, | is a simple bitwise operation with no short-circuiting and no side effects beyond the operands themselves. Performance is typically identical to other arithmetic operations. The decision to use | over || should be driven by the data type and the need for guaranteed evaluation, not by micro-optimization.

Common Mistakes and How to Avoid Them

A frequent mistake is using | when || is intended, especially when migrating from languages like C or C++ where | is often used for bitwise operations and || for logical. In C#, mixing them can lead to unexpected side effects or precedence issues. Another mistake is assuming || works with integers—it doesn't. If you need to combine bitmasks, use |; if you need to combine boolean conditions, use || unless you explicitly need both sides evaluated.

Always consider whether the right operand has side effects. If it does, prefer || to avoid unintended execution, or restructure the code to make the side effects explicit. When using | with booleans, add a comment explaining why short-circuiting is intentionally disabled, so future maintainers understand the decision.

For integer operations, there is no ambiguity: | is the only choice. For boolean expressions, default to || for its short-circuiting behavior, and reserve | for cases where full evaluation is a deliberate requirement. Understanding this distinction prevents subtle bugs and keeps your code predictable.

c# || vs |: Practical Usage and Code Examples | RYUSLOG DEV