Less commonly used C# operators

Posted by: Damir Arh , on 12/31/2018, in Category C#
Views: 20648
Abstract: A tutorial on some less commonly used arithmetic, relational, logical, equality operators in C# and how to use them.

There’s a large collection of operators available in C#.

Many are common to most programming languages and we use them regularly, e.g. arithmetic, relational, logical, and equality operators. Others might not be as generally useful, but can make your code simpler and more efficient in specific scenarios.

The conditional operator is a great replacement for simple if-else statements used only to assign one or the other value to the same variable.

if (compactMode)
{
    itemsPerRow = 5;
}
else
{
    itemsPerRow = 3;
}

The same result can be achieved using a single expression with the conditional operator.

itemsPerRow = compactMode ? 5 : 3;

This operator is especially convenient when used with simple expressions. Checking if a value is null would be another good example:

middleName = middleName != null ? middleName : "";

In this special case, the null-coalescing operator can be used instead.

middleName = middleName ?? "";

Unfortunately, it only works when we want to directly use the value being checked for null. If we want to access one of its members, we still need the conditional operator.

count = list != null ? list.Count : 0;

To simplify such cases, the null conditional operator was introduced in C# 6.

count = list?.Count ?? 0;

It allows safe member access. If the left-hand side value is non-null, it accesses the member. Otherwise it just returns null avoiding the NullReferenceException which would be thrown when trying to access a member of a null-valued variable.

Its usage is not restricted to properties. It also works with fields, methods and even index access.

int? firstValue = list?[0];

The null conditional operator is also a great choice for thread-safe invoking of event handlers.

In earlier versions of C#, the event handler had to be stored in a local variable to be safe in multi-threaded scenarios. Otherwise its value could be changed by another thread after it was checked for null but before it was invoked, causing a NullReferenceException.

public event PropertyChangedEventHandler PropertyChanged;

private void NotifyPropertyChanged(string propertyName)
{
    var handler = PropertyChanged;
    if (handler != null)
    {
        handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

This whole block of code can be replaced with a single line if the null conditional operator is used:

public event PropertyChangedEventHandler PropertyChanged;

private void NotifyPropertyChanged(string propertyName)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

The call is still thread-safe, i.e. the generated code evaluates the variable only once. It then keeps the result in a temporary variable so that it cannot be changed afterwards.

Bitwise operators are specialized operators for bit manipulation of 32-bit (int and uint) and 64-bit (long and ulong) numeric values. Let’s take advantage of binary literals and digit separators (both were added in C# 7.0) to see how they behave:

int a = 0b00000000_00000000_00000000_00001111;
int b = 0b00000000_00000000_00000000_01010101;

The result of the bitwise AND operator will include the bits which are set in both operands.

var and = a & b; // = 0b00000000_00000000_00000000_00000101

The result of the bitwise OR operator will include the bits which are set in at least one operand.

var or = a | b; // = 0b00000000_00000000_00000000_01011111

The result of the bitwise XOR (exclusive or) operator will include the bits which are set in exactly one operand.

var xor = a ^ b; // = 0b00000000_00000000_00000000_01011010

The result of the bitwise complement operator will include the bits which are not set in its (single) operand.

var complement = ~a; // = 0b11111111_11111111_11111111_11110000

The bitwise shift operators shift the bits in the binary representation to the left or to the right by the given number of places.

var shiftLeft = a << 1; // = 0b00000000_00000000_00000000_00011110
var shiftRight = a >> 1; // = 0b00000000_00000000_00000000_00000111

The bits which move past the end, do not wrap around to the other side. Hence, if we shifted any value far enough to the left or to the right, the result will be 0.

var multiShift = 0b00000000_00000000_00000000_00000001;
for (int i = 0; i < 32; i++)
{
    multiShift = multiShift << 1;
}

However, if we use the second operand to shift the bits by the same number of places in a single step, the result won’t be the same.

var singleShift = 0b1 << 32; // = 0b1

To understand this behavior, we must look at how the operator is defined.

Before shifting the bits, the second operand will be normalized to the bit length of the first operand with the modulo operation, i.e. by calculating the remainder of dividing the second operand by the bit length of the first operand. In our example, the first operand is a 32-bit number, hence 32 % 32 = 0. The number will be shifted left by 0 places, not 32.

The bitwise AND, OR and XOR operators also serve as logical operators when applied to bool operands.

var a = true;
var b = false;
var and = a & b; // = false
var or = a | b; // = true
var xor = a ^ b; // = true

Although these AND and OR operators return the same result as the corresponding conditional operators (&& and ||), they don’t behave identically. Operators & and | are eager, i.e. they will evaluate both operands even if the result can be determined after evaluating the first operand. Operators && and || are lazy, i.e. they won’t evaluate the second operand when the result can be determined based on the value of the first operand. This can be an important difference when operands are method calls instead of simple variables:

var eager = true | IsOdd(1); // IsOdd will be invoked
var lazy = true || IsOdd(1); // IsOdd won't be invoked

All arithmetic and bitwise operators also have a corresponding shorthand assignment operator for the case when the calculated value is assigned to the first operand.

a = a + b; // basic syntax
a += b; // shorthand syntax

A special case of arithmetic operators is increment and decrement operators, which respectively increment or decrement the operand value by 1. They are available in two flavors: prefix and postfix. As the name implies, the former changes the operand before returning its value, while the latter first returns the operand value, and then changes it.

var n = 1;
var  prefixIncrement = ++n; // = 2, n = 2
var  prefixDecrement = --n; // = 1, n = 1
var postfixIncrement = n++; // = 1, n = 2
var postfixDecrement = n--; // = 2, n = 1

All four operators are most commonly used in for loops to change the value of the loop variable.

This article was technically reviewed by Yacoub Massad.

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Damir Arh has many years of experience with software development and maintenance; from complex enterprise software projects to modern consumer-oriented mobile applications. Although he has worked with a wide spectrum of different languages, his favorite language remains C#. In his drive towards better development processes, he is a proponent of Test-driven development, Continuous Integration, and Continuous Deployment. He shares his knowledge by speaking at local user groups and conferences, blogging, and writing articles. He is an awarded Microsoft MVP for .NET since 2012.


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!