Every programming language has operators. Operators are the components of the language that, well, operate on variables mostly. Most C# operators are symbols such as % and ??. And some are words, like async and is. In this guide, you’ll see all of them, including an example of each one. We’ll start with the operators that are common among programming languages and end on some that are more specific to C#.
Math and Logic
First off are the several operators for doing math. These are as follows:
It’s worth taking a closer look at a few of these operators.
% Remainder or Modulus
It’s fairly common to use the % operator to identify whether a value is even or odd.
int x = 3; bool isEven = 3 % 2 == 0;
In the above sample, the remainder of 3 divided by 2 is 1. Since it’s not zero, we know that x is an odd number. 4 / 2, on the other hand, has no remainder. Therefore, we can tell that 4 is even.
++ and —
These two operators warrant additional discussion (no pun intended). In languages that lack increment and decrement operators (looking at you SQL), you have to do the following:
int x = 1; x = x + 1;
Luckily, there’s another way to accomplish the same. As we’ve seen in the table of operators above, the += and -= shorten the statement a bit. With those operators, you can do this instead:
int x = 0; x += 1;
And this comes in handy when you need to increment by more than 1, but ++ does the trick succinctly.
Can we Double Down on ++ and –?
Just in case you were wondering, you cannot use ++++ to increment by two. The following fiddle throws a compilation error when we try to do this.
The exact error is:
“The operand of an increment or decrement operator must be a variable, property or indexer”
++ Property
As you can see, we can increment or decrement more than just variables. We can do this with a property or indexer. Here are examples of each:
;
In this example, the Age property initialized with a value of zero. Once it was incremented, it became one.
++ Indexer
The simplest indexer is an array. Let’s increment a value in an array in this next example.
Here, we’ll start with an array with three values: 0,1,2. We’ll increment 1 and decrement 2 so that all values in the array are 1.
Speaking of &&, let’s not get that mixed up with &, which is either a logical or a bitwise operator. Since programming is all about the bits, let’s take a look at the operators that work on bits next. Most of these are fairly standard. But, if you’re coming from a text-based language like VB or SQL that has few symbols, some can be tough to grasp at first. One example is the && instead of and or AND, which can be challenging to remember at first. It becomes muscle memory before long though, I promise!
Bits and Bit-Wise Operators
C# gives you a way to shift bits and perform other bit-wise operations. The following examples will not compile. The bits are lined up to show the effect of the operation.
& and
1 when both bits are 1.
x = 0000 1001 & 0000 1010; x == 0000 1000;
| or
1 when either bit is 1;
x = 0000 1001 | 0000 1010; x == 0000 1011;
^ xor
1 when one bit or the other is 1, but never both.
x = 0000 1001 ^ 0000 1010; x == 0000 0011;
~ bitwise complement
Flips the bits.
x = ~0000 1001; x == 1111 0110
<< bit shift left
Moves the bits left n number of bits. n is 1 in the following example. Higher order bits are truncated.
x = 0000 0001 << 1; x == 0000 0010; 1 << 36 == 16; // because the bits flow back over. 1 << 31 << 35 == 0; // because the higher order bits are truncated.
>> bit shift right
Shifts the bits right by n places. Truncates bits that get pushed off.
x = 0000 0010 >> 1; x == 0000 0001; 1 >> 1 == 0;
Each of these can be combined with an assignment operator. Assume x = 0000 1001;
&=, |=, ^=, ~=, <<=, >>=?
byte x = 1; x <<= 7; x |= 1; x &= 129; x >>= 2; x |= 10; Console.WriteLine(x); // guess the output! // (Answer at the end of this post, no cheating!)
Sure, & is useful for binary math if you’re into that sort of thing, but in C# it has another use. C# has a type of enum that’s called a flags enum, and this is where you can use & and | in your almost everyday code. Let’s say you have a flags enum for the days of the week:
[Flags] enum DaysOfWeek { Sun = 1 << 0, Mon = 1 << 1, Tue = 1 << 2, Wed = 1 << 3, Thu = 1 << 4, Fri = 1 << 5, Sat = 1 << 6 }
Since it’s a flags enum, those are actually the bit representations. Sunday is 1, Monday is 2, Tuesday is 4, and so on. And with that, you can make a workingDays value like so:
var workingDays = (DaysOfWeek) 2 + 4 + 8 + 16 + 32; // or var workingDays = DaysOfWeek.Mon | DaysOfWeek.Tue | DaysOfWeek.Wed | DaysOfWeek.Tue | DaysOfWeek.Fri; // or var weekends = DaysOfWeek.Sun | DaysOfWeek.Sat; var workingDays = ((DaysOfWeek)127) ^ weekends;
And if you want to switch something forward one day, it’s easy enough to use a shift operator.
var appointment = DaysOfWeek.Mon; // move to Tue appointment = << appointment;
Yeah, we just create a value for the appointment on Monday and shift the bit left one to get Tuesday. In binary, that’s doing the following:
Monday: 0000 0010 << Tuesday: 0000 0100
We just shifted all the bits over to the left. If you aren’t careful with bit shifting, you can end up wrapping back to the beginning or truncating bits. And let’s not even get into things like a sign bit and endianness just now. Here’s a fiddle with other operations on the DaysOfWeek enumeration.
So the bit operators are there, and they’re pretty darn neat. But you might not really use them in every day C# programming. Let’s move on to some things that you will or at least should be using just about every day.
Type Operators
Since C# is a strongly typed language, it has some operators that operate on types. From type checking to casting, these operators make it easier to work with polymorphic code.
Cast
There are two operators for casting from a more basic type to a more specific type. The hard cast is (T), and it will throw an exception if the cast fails. The other cast operator is as, and it will not fail if the instance can be null. Instead, it will result in a null value if the cast fails. Here are examples of each:
var number = 30000000000000; decimal decimalNumber = (decimal)number; IDisposable someObject = new MyDisposable(); MyDisposable casted = (MyDisposable)someObject; Assert.Throws(() => (NotMyDisposable)someObject); NotMyDisposable somethingElse = someObject as NotMyDisposable; Assert.Null(somethingElse);
Or you can check out this fiddle for a working sample.
As you can see, type casting can get a bit hairy if you don’t think of the consequences. There are other type-based operators to help.
Type Operators
The new operator creates an instance of a type. Use it as follows:
var car = new Car(); // or var car = new Car{ Miles = 6000 }
There’s the typeof and sizeof operators, which get the type and size of an instance.
System.Type t = typeof(car); var size = sizeof(car);
Let’s look at typeof in the sense of type checking. You might think you’d have to do this:
if(car.GetType() == typeof(Racecar)) { car.Race(); }
But actually, there’s an operator for that. The operator is does a type check like this:
if(car is Racecar) { car.Race(); }
And it gets even better because you can check for base classes and interface implementation, too.
if(car is IRaceable) { ((IRaceable)car).Race(); }
And default returns the default value of the given type or the implied type. This is especially useful when working with generics!
int zero = default; var zero = default(long); var willBeNull = default(Car); T ParseToMyType<T>(string inputValue) { if(string.IsNullOrEmpty(inputValue)) return default(T); return ParseLib.Parse<T>(inputValue); }
Accessing type members is done in a few ways. They’re shown below with comments on what will happen.
car.Miles; // get the miles car.Go(); // call method Car.Build() // call a static method cars[n]; // access an index of an indexer type like an array
Conditionals
And for working with null values, C# introduced the wonderful ? (null conditional) and ?? (null coalescing operator).
- ?.
- ?[]
- ??
Here are some examples:
car?.Go(); // won't throw null exception if car is null cars?[n]; // won't throw is the cars is null int? x = null; int i = x ?? 0; // will move along past null x and assign 0
What I love most about those two operators is that you can use them in conjunction to make your code nice and clean! Check this out:
int miles = car?.Miles ?? AVG_LOT_MILES;
That little snippet means you don’t have to have multiple lines of conditionals like this simplified example:
int miles; if(car == null) { miles = AVG_LOT_MILES; } else { miles = car.Miles; }
Doesn’t the single line look a lot cleaner? No? You could use the conditional operator ?: as it’s commonly written. The same example would look like this:
int miles = car == null ? AVG_LOT_MILES : car.Miles;
But sometimes the first way is nicer to read. Either way, you’ve got choices, which can help clean up your code.
Lambda, Lambda, Delegate
For a while, there was only the delegate operator if you wanted to pass a function to an iterator. But with the introduction of the lambda operator back in C# 3.0, it’s now just a matter of using an arrow => function.
Array.ForEach<int>( new int[10], (x) => Console.Write(x) );
And besides the basic delegate, I find myself using the generic Action and Func delegates much more frequently. I suppose there are times when I should consider using a more strongly typed delegate, such as the following:
// create a delegate type delegate void Log(string message); // assign the anonymous delegate with the same signature as the delegate type Log log = delegate(message) { Console.WriteLint(message); } // but it's also quite easy to use Action<string> Action<string> log = delegate(string message) { Console.WriteLine(message); }; log("HI"); log = delegate(string message) { Console.WriteLine(); Console.WriteLine(message); }; log("Do you like my hat?"); log("BYE!");
As you can probably guess, it’s clearer to pass a typed delegate, just like it’s clearer to use a strongly named type instead of a built-in one. This is similar to how you would use EndDate instead of DateTime for an end date. You’ll end up with fewer errors in your code.
Operator, I’ll Call You Later
So I didn’t talk about checked and unchecked, and that’s because I’ve never actually used them. The default is unchecked, but you can check for overflow in an integer assignment by using checked. You know, in case the compiler doesn’t warn you already.
Before we wrap it up, there’s just one more thing to do. Did you solve the binary problem in this post? If you gave it a shot (in your head or on paper only please), you should’ve arrived at the answer to everything. Have fun, enjoy life, and happy coding!
You might also be interested in further reading about other C# language concepts:
- Enums
- Lists
- Strings
- Arrays
- Dictionaries
- Interfaces
- Switch Statements
- Classes
- Constructor
- Inheritance
- Operators
Learn more how CodeIt.Right can help you automate code reviews and improve the quality of your code.
8 Comments. Leave new
Missed the true and false operators: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/true-false-operators
There’s a typo in the “| or” example where the “& and” is being used instead.
Very nice article, just a small error on the OR example, it’s the same as the AND.
So true…should be:
x = 0000 1001
| 0000 1010;
x == 0000 1011;
And I like the looks of that bool operator! Operator overloading is a whole other (totally related) story.
Assignment operator “=” also returns the assigned value. Like “var x = y = z;” And one < is missing on left shift.
on right shift sorry.
Good catch!
The increment operator shows an example of x- and the decrement operator shows an example of -x (somehow missing the second -). The descriptions and examples also seem to match the ideas of post-increment/decrement (for “increment”) and pre-increment/decrement (for “decrement”).