C# : 'is' keyword and checking for Not
Asked Answered
B

14

328

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...
Barricade answered 1/5, 2009 at 14:34 Comment(6)
I personally like the "child isnotafreaking ...". I'm voting to have that keyword put into C# 5Illogic
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.Brabazon
@MartinPeck, there might not be an else clause. That's the reason I searched for this.Bourgeoisie
@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.Multiplication
maybe what we need in general are ifnot statementsKerek
The Inform programming language (for interactive fiction) support the keyword "has" in order to know whether an object provides a given attribute, and also its inverse, "hasnt". For me, it is more readable "obj hasnt edible" than "not( obj has edible )" or its C-syntax-like equivalent, "!( obj has edible)".Ipomoea
M
333
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...
Milord answered 1/5, 2009 at 14:35 Comment(3)
ck: I meant in the sense of operators, there's no IsNot thing.Milord
Yes. I am kidding in case it is not obvious.Milord
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 hahaReinert
T
118

You can do it this way:

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

if (a is TextReader == false)
{
   Console.WriteLine("failed");
}
Trapezoid answered 1/5, 2009 at 14:47 Comment(6)
@Frank - yep, the is keyword gives a boolean, which you can compare to falseTrapezoid
@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 !.Milord
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.Kerek
@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)Shekinah
@Shekinah 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.Kerek
@Shekinah 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.Cochrane
H
88

New In C# 9.0

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

if (part is not IContainer)
{
    return;
}

Original Answer

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;
}
Hemo answered 23/1, 2019 at 20:15 Comment(3)
Similarly you could do if (part as IContainer is null). Honestly not sure which is better.Cubitiere
@Cubitiere 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.Selfmastery
True, but my comment was prior to C#9's release, which has a far more readable alternative :)Cubitiere
A
21

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) { ... }
Afroasian answered 28/5, 2020 at 6:16 Comment(2)
Can't we use is {} instead of is not null?Peel
Yes, child is {} is also valid, but not as clear as child is not null in my opinion.Afroasian
E
11

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
}
Earlie answered 1/5, 2009 at 14:45 Comment(0)
O
10

Why not just use the else ?

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

Its neat it familiar and simple ?

Outdoor answered 1/5, 2009 at 14:38 Comment(2)
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.Barricade
With the sample code in the question, this would mean an empty if bracket. That doesn't sound like a sensible alternative.Multiplication
P
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
}
Prosaism answered 1/5, 2009 at 14:38 Comment(6)
@Mehrdad - Nullable ? would enable it to work, not that this should be used. It's just an example of an uglier way.Sacculus
@Steveo3000: Yes, but you should explicitly mention ? is the as clause. obj as int is a always a compile time error.Milord
@Mehrdad - Agreed, BFree could edit his post to reflect this. Giving us 'obj as int?'.Sacculus
@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.Milord
you could optionally do if (obj == default(IContainer)), which would take care of value types and reference typesIllogic
@Joseph: No. The problem is not with the if line. The problem is that as operator expects either a value-type or a Nullable<T> as the target type. Otherwise, it's a compile time error.Milord
F
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?

Frangipane answered 1/5, 2009 at 14:41 Comment(3)
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.Barricade
Okay I understand. From your examples I got the impression that you were looking for a new, dedicated operator.Frangipane
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.Ground
A
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.

Alrzc answered 1/5, 2009 at 16:34 Comment(0)
L
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.

Lewls answered 7/3, 2018 at 16:12 Comment(0)
R
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 {}
Ralaigh answered 1/5, 2009 at 14:40 Comment(0)
S
0

I stumbled upon this seeing as is not had been added. Since generics is used in a some of the answers provided, I thought maybe the use of it means you may not even need to test. This will work for the original problem but it is not an all encompassing solution. You will still need to use is not for other situations.

Say you specify DocumentPart as being of type IContainer only.

public class  DocumentPart<T> where T : IContainer
{
}

Then the update method will look like below

public void Update(DocumentPart<IContainer> part)
{
}

You're then forced to use IContainer or an instance(of class) that is covertible to IContainer like

public void Update(DocumentPart<ContainerChildOne> part)
{
}

or

public void Update(DocumentPart<ContainerChildTwo> part)
{
}

It won't solve for if you want to use something other than IContainer Based on the do nothing and return statement in the op's code, I would write it like above.

Slighting answered 28/8, 2023 at 2:51 Comment(0)
I
-2

Ill use this

If(!(object is Car)){

}

Impressment answered 8/9, 2021 at 0:49 Comment(1)
Please add further details to expand on your answer, such as working code or documentation citations.Cowled
D
-3
if (child is IContainer ? false : true)
Deledda answered 23/4, 2014 at 16:43 Comment(1)
You should give an exaplanation of your code.Ravens

© 2022 - 2024 — McMap. All rights reserved.