10 Lesser-Known C# Operators That Will Surprise You (With Examples)
When most developers think of C# operators, they picture the basics: +
, -
, *
, /
, =
, ==
. But beyond the everyday syntax, C# has a rich set of specialized operators that can simplify your code, reduce bugs, and even improve performance in certain scenarios.
Many of these operators are underutilized because they don’t get much attention in beginner tutorials. In this post, we’ll take a deep dive into 10 lesser-known C# operators, explain how they work, why they’re useful, and provide clear “old way vs new way” code comparisons so you can see exactly why these operators exist.
Note that there’s a “Sidebar” section toward the end with more information about what the question mark operators are used for.
1. Null-Coalescing Operator (??
)
The null-coalescing operator returns the left-hand operand if it isn’t null
. Otherwise, it returns the right-hand operand.
Old Way
The old way required an “if” check for null
New Way
Notice that the “if” check for null is no longer required
✅ Cleaner, less typing, and avoids repetitive null checks.
2. Null-Coalescing Assignment (??=
)
Introduced in C# 8.0, this operator assigns a value only if the variable is currently null
.
Old Way
Another unnecessary check for null
New Way
More concise syntax for assignment when null
✅ Compact, perfect for lazy initialization.
3. Null-Conditional Operator (?.
)
The null-conditional operator (sometimes called the Elvis operator) safely calls members without throwing a NullReferenceException
.
Old Way
Annoying nested checks for null
New Way
More concise syntax
✅ One operator replaces multiple nested checks.
4. Pattern Matching with is
and when
Modern C# extends the is
operator to support pattern matching.
Old Way
A cast was required previously
New Way
No explicit casting required
✅ Combines type check + safe cast + condition in one statement.
5. nameof
Operator
The nameof
operator returns the name of a variable, property, or type as a string—safer than hardcoding.
Old Way
Using hard coded strings is not ideal- programs change over time
New Way
Now using the “nameof” operator
✅ Refactor-safe: if you rename FirstName
, the compiler updates it automatically.
6. checked
and unchecked
These operators give you explicit control over numeric overflow.
Old Way (Silent Bug)
Remember that ints have a fixed number of bits and can max out in value. They overflow when you exceed that, very bad…
New Way
Now you can at least be notified if overflow occurs
✅ Makes overflow behavior explicit.
7. stackalloc
The stackalloc
operator allocates memory on the stack instead of the heap.
Old Way (Heap Allocation)
Array is allocated on the heap by default
New Way (Stack Allocation)
Now the array is placed on the stack
✅ Faster, avoids garbage collection.
⚠️ Use only for small buffers due to limited stack size.
8. The ->
Operator (Unsafe Code)
The ->
operator is shorthand for accessing members via a pointer.
Old Way
Dereferencing a pointer to access and manipulate the pointed to object
New Way
Arrow syntax to access data
✅ Cleaner syntax for unsafe pointer operations.
9. Ternary Conditional Operator (?:
)
Inline conditional expressions.
Old Way
Using a conditional “if/else” statement
New Way
Concise syntax with the ternary operator
✅ Perfect for compact logic.
10. default
Literal
Introduced in C# 7.1, this operator produces the default value of a type.
Old Way
Note that the variable type is passed to default
New Way
The type of the value can be inferred from the variable type on the left hand side
✅ Cleaner, especially in generics.
Sidebar: What Does ?
After a Type Mean in C#?
You’ll notice some examples use int?
or string?
. The ?
means nullable type:
For value types (e.g.,
int?
,bool?
) → allowsnull
(normally value types cannot be null).For reference types (e.g.,
string?
) → in modern C# with nullable annotations, the?
means “this reference may legally be null.”
Examples:
Adding the question mark to the variable type denotes that it is nullable, or able to accept null as a value
Why would you want that?
Here are some common use cases:
1. Optional data in databases or APIs
When mapping to a database column that allows nulls, or consuming an API where a field may be missing, you need a way to represent “no value.”
int? middleNameId = null; // optional foreign key
2. Uninitialized state
Sometimes you can’t assign a value until later. Instead of using a “magic number” like -1
, nullable types let you explicitly express “not yet set.”
int? score = null; // hasn't been calculated yet
3. Distinguishing between “zero” and “nothing”
In some domains, 0
has meaning (“paid nothing”), while null
means the data isn’t known yet.
decimal? amountPaid = null; // payment not reported yet
4. User input and forms
When users can leave fields blank, string?
lets you clearly model optional inputs.
string? phoneNumber = null; // optional form field
5. Expressive domain models
By marking properties as nullable, your code communicates intent clearly—no need for hidden assumptions or special comments.
public class Customer
{
public string Name { get; set; } = string.Empty;
public string? MiddleName { get; set; } // explicitly optional
}
✅ Summary
Use
int?
when a number might not exist yet.Use
string?
when text might be missing or optional.Nullable types reduce reliance on placeholder values and make your code clearer, safer, and closer to the real-world domain you’re modeling.
Conclusion
We’ve explored 10 C# operators that don’t always get the spotlight but can make a huge difference in how you write code:
??
,??=
, and?.
→ safer, cleaner null handling.nameof
,checked
, anddefault
→ fewer bugs and easier refactoring.stackalloc
and->
→ advanced performance features.?:
and pattern matching → concise, expressive logic.
Many of these operators shine brightest when paired with nullable types (T?
). By allowing your code to explicitly represent “no value,” you avoid placeholder hacks like -1
or empty strings, and instead model your problem domain more truthfully. Operators like ??
and ?.
then become natural tools for working with those nullable values safely and clearly.
The result? Code that’s not just functional, but also:
Expressive → communicates intent directly in the type system.
Resilient → avoids common pitfalls like
NullReferenceException
.Efficient → in both writing and execution.
By mastering these lesser-known operators, you’ll level up from writing code that merely works to writing code that’s idiomatic, safe, and built to last.