1

In C# there is is operator for checking if object is compatible with some type. This operators tries to cast object to some type and if casting is successful it returns true (or false if casting fails).

From Jeffrey Richter CLR via C#:

The is operator checks whether an object is compatible with a given type, and the result of the evaluation is a Boolean: true or false.

if (o is Employee) 
{
    Employee e = (Employee) o;
    // Use e within the remainder of the 'if' statement.
}

In this code, the CLR is actually checking the object’s type twice: The is operator first checks to see if o is compatible with the Employee type. If it is, inside the if statement, the CLR again verifies that o refers to an Employee when performing the cast. The CLR’s type checking improves security, but it certainly comes at a performance cost, because the CLR must determine the actual type of the object referred to by the variable (o), and then the CLR must walk the inheritance hierarchy, checking each base type against the specified type (Employee).

Also, from the same book:

Employee e = o as Employee;
if (e != null) 
{
    // Use e within the 'if' statement.
}

In this code, the CLR checks if o is compatible with the Employee type, and if it is, as returns a non-null reference to the same object. If o is not compatible with the Employee type, the as operator returns null. Notice that the as operator causes the CLR to verify an object’s type just once. The if statement simply checks whether e is null; this check can be performed faster than verifying an object’s type.

So, my question is: why do we need is operator? Which are the cases when is operator is more preferable over as.

Dixy
  • 202
  • 2
  • 9
  • 4
    You can't use `as` with value types, but you can do something like `foo as int?` – Lucas Trzesniewski Feb 09 '17 at 13:59
  • 8
    The two do a different thing. `is` asks if the instance is based on a given type. `as` makes it a given type. They are semantically entirely unrelated. Nobody is saying that every `is` must be followed by a cast :) – Luaan Feb 09 '17 at 14:00
  • 2
    @Luaan unrelated? Not so sure: [Is is as or is as is?](https://blogs.msdn.microsoft.com/ericlippert/2010/09/16/is-is-as-or-is-as-is/) – Lucas Trzesniewski Feb 09 '17 at 14:02
  • 2
    Note that the underlying IL is going to use the `isinst` instruction for both `is` and `as`. – Lasse V. Karlsen Feb 09 '17 at 14:04
  • I'd imagine a scenario where you do not actually want to cast some object to the compatible object but just want to filter out what objects are compatible or what objects are not compatible and afterwards pass them to another application or just store them as they are and later on someone has to deal with the type conversion. Other than that the performance of as will always be better than the performance of is and typecast because is also does the typecast but without returning you the castet object. – Paul Weiland Feb 09 '17 at 14:11
  • @Luaan, @PaulWeiland, IL calls `isinst Employee` for both `is` and `as` so, you try to cast object to ome type in both cases, but with `is` you return `true` or `false` with `as` - reference or `null` – Dixy Feb 09 '17 at 14:11
  • @Dixy I know, I just provided a use case where you do not actually want to have a reference or null but you just need true or false to then decide if you want to throw the objects away or not. – Paul Weiland Feb 09 '17 at 14:15
  • That has nothing to do with the *semantics* of its use, Dixy. Just because the implementation is the same doesn't mean the meaning is the same. – Luaan Feb 09 '17 at 14:15
  • That answers the "why do we need is operator?" part of your question.Of course one could argue in the end this question is absolutely opinion based. – Paul Weiland Feb 09 '17 at 14:17
  • 2
    You don't technically need it, since you can write code to do the same thing in other ways. Therefore it is just a matter of convenience - and the answer to this question is thus primarily opinion based. – Matthew Watson Feb 09 '17 at 14:27

4 Answers4

8

why do we need is operator?

We don't need it. It is redundant. If the is operator were not in the language you could emulate it by simply writing

(x as Blah) != null

for reference types and

(x as Blah?) != null

for value types.

In fact, that is all is is; if you look at the IL, both is and as compile down to the same IL instruction.

Your first question cannot be answered because it presumes a falsehood. Why do we need this operator? We don't need it, so there is no reason why we need it. So this is not a productive question to ask.

Which are the cases when is operator is more preferable over as.

I think you meant to ask

why would I write the "inefficient" code that does two type checks -- is followed by a cast -- when I could write the efficient code that does one type check using as and a null check?

First of all, the argument from efficiency is weak. Type checks are cheap, and even if they are expensive, they're probably not the most expensive thing you do. Don't change code that looks perfectly sensible just to save those few nanoseconds. If you think the code looks nicer or is easier to read using is rather than as, then use is rather than as. There is no product in the marketplace today whose success or failure was predicated on using as vs is.

Or, look at it the other way. Both is and as are evidence that your program doesn't even know what the type of a value is, and programs where the compiler cannot work out the types tend to be (1) buggy, and (2) slow. If you care so much about speed, don't write programs that do one type test instead of two; write programs that do zero type tests instead of one! Write programs where typing can be determined statically.

Second, there are situations in C# where you need an expression, not a statement, and C# unfortunately does not have "let" expressions outside of queries. You can write

... e is Manager ? ((Manager)e).Reports : 0 ...

as an expression but pre C# 7 there was no way to write

Manager m = e as Manager;

in an expression context. In a query you could write either

from e in Employees
select e is Manager ? ((Manager)e).Reports : 0

or

from e in Employees 
let m = e as Manager
select m == null ? 0 : m.Reports

but there is no "let" in an expression context outside of queries. It would be nice to be able to write

... let m = e as Manager in m == null ? 0 : m.Reports ...  

in an arbitrary expression. But we can get some of the way there. In C# 7 you'll (probably) be able to write

e is Manager m ? m.Reports : 0 ...

which is a nice sugar and eliminates the inefficient double-check. The is-with-new-variable syntax nicely combines everything together: you get a Boolean type test and a named, typed reference.

Now, what I just said is a slight lie; as of C# 6 you can write the code above as

(e as Manager)?.Reports ?? 0

which does the type check once. But pre C# 6.0 you were out of luck; you pretty much always had to do the type check twice if you were in an expression context.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
2

With C# 7 operator is can be less wordy then as

Compare this

Employee e = o as Employee;
if (e != null) 
{
    // Use e within the 'if' statement.
}

and this

if (o is Employee e) 
{
    // Use e within the 'if' statement.
}

Information from here. Section Pattern Matching with Is Expressions

Disappointed
  • 1,100
  • 1
  • 9
  • 21
1

There are times when you might want to just check the type not actually go through the effort of casting it.

As such you can just use the is operator to confirm your object is compatible, and do whatever logic you want. Whereas in other scenarios you may just want to cast (Safely or otherwise) and utilise the returned value.

Ultimately because is just returns a boolean, you can use it for checking. as and the (T)MyType type casting are used to safely casting to null, or throwing an Exception respectively

How to: Safely Cast by Using as and is Operators (C# Programming Guide)

JEV
  • 2,494
  • 4
  • 33
  • 47
  • 1
    Not sure why this was downvoted... Seems like a valid point. – Matthew Watson Feb 09 '17 at 14:05
  • `There are times when you might want to just check the type not actually go through the effort of casting it` - it is wrong. `is` tries to cast object to some type and return `true` or `false`. – Dixy Feb 09 '17 at 14:16
  • @Dixy What makes you think it tries to cast the object? The C# language specification, section 7.10.10 does not mention anything to do with attempting to make a cast. – Matthew Watson Feb 09 '17 at 14:19
  • 1
    @MatthewWatson, you can see the IL generated by `is` and `as`. Both call `isinst` – Dixy Feb 09 '17 at 14:22
  • @Dixy Aha, you're right - in fact [the documentation for `isinst`](https://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.isinst.aspx) does mention this too. – Matthew Watson Feb 09 '17 at 14:23
1

At least one use-case I can think of is when comparing if a certain variable is a value type (as cannot be used in that case).

For instance,

var x = ...;
if(x is bool)
{
    // do something
}

It can also be useful when you don't necessarily need to use the cast, but are simply interested whether or not something is of a certain underlying type.

kha
  • 19,123
  • 9
  • 34
  • 67
  • But in this case you do casting to bool and forget about it. You can replace your `if` by `if (x.GetType() == typeof(bool))` without casting - only check types – Dixy Feb 09 '17 at 14:14
  • @Dixy not quite true. What if `x` is null? you "could" in theory do `if (x != null && x.GetType() == typeof(bool))` but why type so many letters when you can just use `is`? – kha Feb 09 '17 at 14:16
  • 1
    @Dixy: Do not get in the habit of replacing `is` with `GetType` because they are not the same. For instance, suppose `Manager` is a subtype of `Employee`. `object o = new Manager(); ... if (o is Employee)` is true but `if (o.GetType() == typeof(Employee)` is false. – Eric Lippert Feb 09 '17 at 15:11
  • @EricLippert, I know that it is not the same, it was replacing only for code in answer. To check if it is in inheritance hierarchy I can use `IsAssignableFrom()` – Dixy Feb 09 '17 at 15:24
  • 1
    @Dixy: Do not get in the habit of using `IsAssignableFrom()` to check to see if one thing is in the inheritance hierarchy of another, because that is not what `IsAssignableFrom()` does. It checks assignability. For example, `IEnumerable` is not in the inheritance hierarchy of `List`, but it is assignable. – Eric Lippert Feb 09 '17 at 15:27
  • 1
    @Dixy: More generally: **believe what it says on the label**. You should be using `IsAssignableFrom` to check only one thing: *is this assignable from that?* Things are named according to what they actually do. If you need to check inheritance hierarchy membership then you should write code that actually checks for inheritance hierarchy membership. – Eric Lippert Feb 09 '17 at 15:28