In this article, we are going to learn about the type-testing “as” and “is” operators in C#.
Sometimes, when we code in a strongly typed language like C#, the need arises to mix and match a little bit. That’s okay, as we can’t always foresee the path that the problem-solving aspect of programming will take us on. But whenever this happens, we must pick our options wisely.
For this reason, today we are going to see one of the many ways of casting and comparing types in C#: the type-testing operators.
Let’s start.
The “Is” Operator
The is
keyword represents a built-in type comparison operator. As such, it will always return a boolean value when used. The way we use it is pretty similar to the basic ==
equality operator that everyone knows and loves, and both can be used to compare types:
var example = "This is a string"; var equalityComparison = example.GetType() == typeof(string); var typeComparison = example is string;
Both expressions represent valid ways to check if a given object is of a certain type. But doing this type of comparison using the is
operator is much easier than doing it with the ==
operator. It’s also easier for a human to read, which is something we should always strive for when writing code.
However, we should keep in mind that some cases in which the is
operator will return true
may not be evident at first. Consider an Animal
class with a property that defines its eating habits, and a Cow
class that inherits from the Animal
class:
public class Animal { public string EatingHabits { get; set; } public Animal(string eatingHabits) { EatingHabits = eatingHabits; } } public class Cow : Animal { public Cow() : base(eatingHabits: "herbivore") { } }
If we compare them using the equality (==
) operator in the way we did before, we’ll get a false
value, as the equality operator works in a strict way.
But we must take into consideration that the is
operator will evaluate the possibility of implicit reference conversions when comparing values. For this reason, doing the same comparison as before using an is
keyword will inevitably return true
:
var bessie = new Cow(); Console.WriteLine(bessie.GetType() == typeof(Animal)); Console.WriteLine(bessie is Animal);
If we run the example code, we’ll see that an is
comparison will also return true
when an expression returns a type that inherits the type it’s being compared to, as the C# inheritance is a type of implicit reference type conversion.
If you want to know more about implicit conversions, please check out our article on C# Type Conversion. We also have an article on C# Inheritance if you want to know more.
The “As” Operator
The first thing you need to know about the as
keyword is that it is a type conversion operator instead of a type comparison one. As such, the cases in which it can be used are pretty different from the is
keyword.
The as
operator will work for converting either reference types or nullable value types into other types:
var bessie = new Cow(); var bessieAnimal = bessie as Animal; Console.WriteLine(bessieAnimal is not null);
Since the Cow
class that we’ve created in the previous section is a reference type that inherits from the Animal
class, we didn’t have problems when trying to make the conversion.
Contrarily, if we try to convert a generic object into a Cow, things will take a different turn:
var imNotACow = new object(); var notBessie = imNotACow as Cow; Console.WriteLine(notBessie is null ? "The object is null" : "The object is not null");
Running the code above will tell you that the notBessie
object is null
. But it also tells us something rather interesting. Trying to make an invalid conversion using the as
keyword will never throw an exception. This is the main difference between using the as
operator and a cast expression.
Considering this fact, using an as
conversion is almost the same as using the following expression:
E is T ? (T)(E) : (T)null
The main difference, in this case, is that the as
operator does the checking and the casting at the same time. It’s also pretty interesting to see a connection between the two operators we’ve learned about today!
When to Use the As and Is Operators
Okay, now we know what are the as
and is
operators, but still don’t know much about when to use them, right?
Well, don’t worry about that.
It’s time for us to work on some of the use cases that make the most sense for each of the type-testing operators we’ve learned today.
When we think about the is operator, we see that its main use is comparing types in a readable manner. Also, we may have an application in which we want to make a non-strict type comparison. That’s where the is
operator shines the brightest.
But that’s not all!
If you want, you can also check for null values in a more human-friendly way:
static string HumanFriendlyNullCheck(object obj) { return obj is null ? $"This {typeof(object)} is null" : $"This {typeof(object)} is not null"; }
As for the as
operator, its main real-life use case is when you want to avoid exception handling, since checking for null
values will generate much less overhead than throwing exceptions. It’s also simpler to code:
var imNotACow = new object(); var notBessieAs = imNotACow as Cow; if (notBessieAs is null) { Console.WriteLine("This verifies that a conversion was not valid using an 'as' operator"); } try { var notBessieCast = (Cow)imNotACow; } catch (Exception) { Console.WriteLine("This verifies that a conversion was not valid using a cast expression"); }
There may be other cases when the operators you’ve learned about today overlap with their counterparts. In these cases, we can follow our personal preferences or the guidelines established by our teams.
Conclusion
In this article, we have learned how the “is” operator works, how the “as” operator works, and when we should use each one.
That’s all from this one and we hope to see you again in the next article!