305

This is a silly question, but you can use this code to check if something is a particular type...

if (child is IContainer) { //....

Is there a more elegant way to check for the "NOT" instance?

if (!(child is IContainer)) { //A little ugly... silly, yes I know...

//these don't work :)
if (child !is IContainer) {
if (child isnt IContainer) { 
if (child aint IContainer) { 
if (child isnotafreaking IContainer) { 

Yes, yes... silly question....

Because there is some question on what the code looks like, it's just a simple return at the start of a method.

public void Update(DocumentPart part) {
    part.Update();
    if (!(DocumentPart is IContainer)) { return; }
    foreach(DocumentPart child in ((IContainer)part).Children) {
       //...etc...
Venkat
  • 2,398
  • 2
  • 20
  • 51
hugoware
  • 33,265
  • 24
  • 58
  • 70
  • 118
    I personally like the "child isnotafreaking ...". I'm voting to have that keyword put into C# 5 – Joseph May 01 '09 at 14:39
  • I'm interested to know the situation you'd use this? What does the "else" part of this code look like and can't you just invert the test? If your code is saying "if child isn't an IContainer then throw exceptions" or "if child isn't an IContainer then maybe it's an IFoo so I'll try that next" then isn't there an implied else statement there? I'm probably missing something. – Martin Peck May 01 '09 at 14:52
  • 1
    @MartinPeck, there might not be an else clause. That's the reason I searched for this. – Joshua Walsh Apr 16 '15 at 04:45
  • 1
    @MartinPeck here's a sample: `if (!(argument is MapsControlViewModel vm)) { return; }` - I could invert the if and put the whoooole rest of the method inside the if's brackets, but then I'd get Christmas-tree code, with a lot of closing brackets at the end of the method. That's much less readable. – ANeves thinks SE is evil Feb 09 '18 at 16:05
  • 3
    maybe what we need in general are `ifnot` statements – Dave Cousineau Oct 11 '18 at 17:25

12 Answers12

316
if(!(child is IContainer))

is the only operator to go (there's no IsNot operator).

You can build an extension method that does it:

public static bool IsA<T>(this object obj) {
    return obj is T;
}

and then use it to:

if (!child.IsA<IContainer>())

And you could follow on your theme:

public static bool IsNotAFreaking<T>(this object obj) {
    return !(obj is T);
}

if (child.IsNotAFreaking<IContainer>()) { // ...

Update (considering the OP's code snippet):

Since you're actually casting the value afterward, you could just use as instead:

public void Update(DocumentPart part) {
    part.Update();
    IContainer containerPart = part as IContainer;
    if(containerPart == null) return;
    foreach(DocumentPart child in containerPart.Children) { // omit the cast.
       //...etc...
mmx
  • 390,062
  • 84
  • 829
  • 778
  • 2
    ck: I meant in the sense of operators, there's no `IsNot` thing. – mmx May 01 '09 at 14:56
  • 5
    Yes. I am kidding in case it is not obvious. – mmx May 30 '14 at 21:04
  • I knew `not` hadn't existed in C#. But one day I typed in "not" anyways thinking I could just fix it later. Imagine my suprise when visual studio showed `not` as a valid keyword haha – fjch1997 Dec 26 '20 at 02:51
113

You can do it this way:

object a = new StreamWriter("c:\\temp\\test.txt");

if (a is TextReader == false)
{
   Console.WriteLine("failed");
}
cjk
  • 43,338
  • 9
  • 74
  • 109
  • 2
    @Frank - yep, the is keyword gives a boolean, which you can compare to false – cjk May 01 '09 at 15:13
  • 32
    @Frank it works because `is` has higher precedence relative to `==`. The only reason you can't use `!x is f` is that it has less precedence than `!`. – mmx May 01 '09 at 15:23
  • I like this but it doesn't seem to work right when introducing a variable, even though it should. `if (a is TextReader reader == false)` "should" work, but it won't let you use the variable in the true path saying it might not have been initialized. – Dave Cousineau Oct 11 '18 at 17:21
  • @DaveCousineau - Normally you would typecheck and introduce a variable when you want to use the introduced variable. I'm not sure how the variable would be useful if the typecheck failed.(disclaimer - I find the "Pattern Matching" feature both poorly named and as bad of a code smell as using `out` parameters) – StingyJack Jan 19 '19 at 02:54
  • @StingyJack there is some kind of glitch where in the *true path*, the variable is considered uninitialized. even if you say `if (a is TextReader reader == true)` it thinks the variable is uninitialized. – Dave Cousineau Jan 21 '19 at 17:10
  • @StingyJack The "out Type variable" and "is Type variable" constructions makes your code much clearer. It's a good practice to declare variables as late as possible but double type check (or any double code) should be avoided. The additions you mentioned are very useful. – Peter Krassoi Jul 26 '19 at 08:53
28

This hasn't been mentioned yet. It works and I think it looks better than using !(child is IContainer)

if (part is IContainer is false)
{
    return;
}

New In C# 9.0

https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/#logical-patterns

if (part is not IContainer)
{
    return;
}
Todd Skelton
  • 4,994
  • 2
  • 30
  • 39
  • 4
    Similarly you could do `if (part as IContainer is null)`. Honestly not sure which is better. – Flynn1179 May 14 '19 at 23:13
  • @Flynn1179 That would be less ideal. You're doing a cast then comparison. The one in the answer just does the comparison. Though you could claim better readability, but I think that not comparing to an explicit value would be better in any case. – Jeff Mercado Mar 12 '21 at 07:36
  • True, but my comment was prior to C#9's release, which has a far more readable alternative :) – Flynn1179 Mar 12 '21 at 07:40
10

Why not just use the else ?

if (child is IContainer)
{
  //
}
else
{
  // Do what you want here
}

Its neat it familiar and simple ?

Mark Broadhurst
  • 2,645
  • 21
  • 45
  • 3
    Nothing wrong with it - this is just a nitpick question. I wanted to immediately exit a function if something was not a particular type. I've done it (!(child is Something)) forever now, but I thought I'd make sure there wasn't a better way. – hugoware May 01 '09 at 14:43
  • 1
    With the sample code in the question, this would mean an empty if bracket. That doesn't sound like a sensible alternative. – ANeves thinks SE is evil Feb 09 '18 at 16:08
10

The way you have it is fine but you could create a set of extension methods to make "a more elegant way to check for the 'NOT' instance."

public static bool Is<T>(this object myObject)
{
    return (myObject is T);
}

public static bool IsNot<T>(this object myObject)
{
    return !(myObject is T);
}

Then you could write:

if (child.IsNot<IContainer>())
{
    // child is not an IContainer
}
Robert Cartaino
  • 25,787
  • 6
  • 42
  • 67
9

C# 9 (released with .NET 5) includes the logical patterns and, or and not, which allows us to write this more elegantly:

if (child is not IContainer) { ... }

Likewise, this pattern can be used to check for null:

if (child is not null) { ... }
Thorkil Holm-Jacobsen
  • 6,190
  • 3
  • 25
  • 40
5

Ugly? I disagree. The only other way (I personally think this is "uglier"):

var obj = child as IContainer;
if(obj == null)
{
   //child "aint" IContainer
}
BFree
  • 97,931
  • 20
  • 150
  • 197
  • @Mehrdad - Nullable ? would enable it to work, not that this should be used. It's just an example of an uglier way. – stevehipwell May 01 '09 at 14:42
  • @Steveo3000: Yes, but you should explicitly mention ? is the `as` clause. `obj as int` is a always a compile time error. – mmx May 01 '09 at 14:45
  • @Mehrdad - Agreed, BFree could edit his post to reflect this. Giving us 'obj as int?'. – stevehipwell May 01 '09 at 14:47
  • @Stevo3000: I don't think anything is wrong with it, however. IContainer feels like an interface rather than value type. Just wanted to point out it requires care on value type and is not always a direct translation of `is` form. – mmx May 01 '09 at 14:49
  • you could optionally do if (obj == default(IContainer)), which would take care of value types and reference types – Joseph May 01 '09 at 15:17
  • @Joseph: No. The problem is not with the `if` line. The problem is that `as` operator expects either a value-type or a `Nullable` as the target type. Otherwise, it's a compile time error. – mmx May 01 '09 at 15:24
3

While this doesn't avoid the problem of parentheses, for the sake of people getting here via Google, it should be mentioned that newer syntax exists (as of C# 7) to make the rest of your code a little cleaner:

if (!(DocumentPart is IContainer container)) { return; }
foreach(DocumentPart child in container.Children) {
    ...

This avoids the double-cast, the null-check, and having a variable available in scopes where it could be null.

StriplingWarrior
  • 135,113
  • 24
  • 223
  • 283
3

The is operator evaluates to a boolean result, so you can do anything you would otherwise be able to do on a bool. To negate it use the ! operator. Why would you want to have a different operator just for this?

Brian Rasmussen
  • 109,816
  • 33
  • 208
  • 305
  • 5
    It's not a different operator. I was wondering if there was a keyword that would let me drop the extra set of parens. It's a major nit-pick, but I was curious. – hugoware May 01 '09 at 14:44
  • Okay I understand. From your examples I got the impression that you were looking for a new, dedicated operator. – Brian Rasmussen May 01 '09 at 15:28
  • I think having such a special operator is bad, because we will have this way (explained this ans, anyway), and if we had another op, then there are two ways to achieve the same thing, can be confusing. – BuddhiP Nov 28 '14 at 12:18
3

The extension method IsNot<T> is a nice way to extend the syntax. Keep in mind

var container = child as IContainer;
if(container != null)
{
  // do something w/ contianer
}

performs better than doing something like

if(child is IContainer)
{
  var container = child as IContainer;
  // do something w/ container
}

In your case, it doesn't matter as you are returning from the method. In other words, be careful to not do both the check for type and then the type conversion immediately after.

Jeff
  • 551
  • 3
  • 8
2

While the IS operator is normally the best way, there is an alternative that you can use in some cirumstances. You can use the as operator and test for null.

MyClass mc = foo as MyClass;
if ( mc == null ) { }
else {}
Muad'Dib
  • 26,680
  • 5
  • 52
  • 68
-3
if (child is IContainer ? false : true)
Ternary
  • 3
  • 1