112

Any easier way to write this if statement?

if (value==1 || value==2)

For example... in SQL you can say where value in (1,2) instead of where value=1 or value=2.

I'm looking for something that would work with any basic type... string, int, etc.

Filburt
  • 17,626
  • 12
  • 64
  • 115
Ricky
  • 1,587
  • 2
  • 12
  • 20
  • 2
    Easier is a personal preference. Personally, I don't think it gets any easier than `value == 1 || value == 2`. – Joel Etherton Oct 11 '10 at 14:51
  • 1
    @Joel: It's easy, but not intuitive to someone who's used to saying "I want it to be 1 or 2". As it turns out, there have been languages which implement precisely this syntax; IBI's FOCUS query lanaguage, for one. – Steven Sudit Oct 11 '10 at 14:53
  • @Steven Sudit - "Intuitive" in that context boils down to personal preference. – Joel Etherton Oct 11 '10 at 15:04
  • @Steven Sudit - I have to admit, I'm fairly amused so far at some of the ridiculous lengths some of the answers are going to just to solve a simple or. – Joel Etherton Oct 11 '10 at 15:06
  • 6
    This is just a basic example of something far more complex. I'm not just working with 2 values. I'm also trying to make some of these lines easier to read. In some cases, I have if statements that span too far, and have dozens of possible values, and long variable names. – Ricky Oct 11 '10 at 15:13
  • @Joel: It's intuitive to speakers of natural languages. It's probably counter-intuitive to seasoned developers. I know for a fact that this syntax leads to ambiguities and is essentially *less* expressive that a typical C-style OR. But, yes, I agree that the answerers are trying too hard and being too clever. It really just boils down to "No, that's not how the language works. Learn to love it, or at least tolerate it." :-) – Steven Sudit Oct 11 '10 at 16:05
  • Joel, you are not thinking in the bigger perspective. what if it was 10 different numbers to match against? – Brian Nov 17 '20 at 09:09

16 Answers16

196

How about:

if (new[] {1, 2}.Contains(value))

It's a hack though :)

Or if you don't mind creating your own extension method, you can create the following:

public static bool In<T>(this T obj, params T[] args)
{
    return args.Contains(obj);
}

And you can use it like this:

if (1.In(1, 2))

:)

Amry
  • 4,791
  • 2
  • 23
  • 24
46

C# 9 supports this directly:

if (value is 1 or 2)

however, in many cases: switch might be clearer (especially with more recent switch syntax enhancements). You can see this here, with the if (value is 1 or 2) getting compiled identically to if (value == 1 || value == 2).

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • I'd like to add that even if `value` is computed inside `if` something like `if (GetValue() is 1 or 2)` the compiler is smart enough to pre-calculate it, and not call `GetValue` twice. Bravo .NET team – Alex from Jitbit May 02 '23 at 17:35
37

A more complicated way :) that emulates SQL's 'IN':

public static class Ext {    
    public static bool In<T>(this T t,params T[] values){
        foreach (T value in values) {
            if (t.Equals(value)) {
                return true;
            }
        }
        return false;
    }
}

if (value.In(1,2)) {
    // ...
}

But go for the standard way, it's more readable.

EDIT: a better solution, according to @Kobi's suggestion:

public static class Ext {    
    public static bool In<T>(this T t,params T[] values){
        return values.Contains(t);
    }
}
Paolo Tedesco
  • 55,237
  • 33
  • 144
  • 193
30

Is this what you are looking for ?

if (new int[] { 1, 2, 3, 4, 5 }.Contains(value))
Homam
  • 23,263
  • 32
  • 111
  • 187
6

If you have a List, you can use .Contains(yourObject), if you're just looking for it existing (like a where). Otherwise look at Linq .Any() extension method.

Nik
  • 2,718
  • 23
  • 34
6

Using Linq,

if(new int[] {1, 2}.Contains(value))

But I'd have to think that your original if is faster.

Joel Rondeau
  • 7,486
  • 2
  • 42
  • 54
  • The performance difference between these two is almost surely irrelevant. The original is more readable, and idiomatic. It's certainly possible to write `Enumerable.Range(0, 10).ToList().ForEach(x => Console.WriteLine(x));` instead of `for(int i = 0; i < 10; i++) { Console.WriteLine(i); }` but that's just going to piss people off. "No one ever writes let `6` be a group." – jason Oct 11 '10 at 15:06
  • @Jason - I agree that if I saw what I wrote in production code, I'd be pissed off. Was more of a "one line to explain a way to do it" than "follow this exactly". Really, I agree with the people suggesting the switch statements. – Joel Rondeau Oct 11 '10 at 15:54
6

Alternatively, and this would give you more flexibility if testing for values other than 1 or 2 in future, is to use a switch statement

switch(value)
{
case 1:
case 2:
   return true;
default:
   return false
}
jules
  • 447
  • 2
  • 11
  • I wouldn't even say "alternatively", this is the closest analogy to the SQL use of IN with a list given in the example (`Contains()` etc. corresponding more with IN against the results of a subquery). – Jon Hanna Oct 11 '10 at 15:11
5

If you search a value in a fixed list of values many times in a long list, HashSet<T> should be used. If the list is very short (< ~20 items), List could have better performance, based on this test HashSet vs. List performance

HashSet<int> nums = new HashSet<int> { 1, 2, 3, 4, 5 };
// ....
if (nums.Contains(value))
detale
  • 12,482
  • 4
  • 41
  • 42
  • :) This is an interesting statement about `HashSet`... Considering it is named after its data storage implementation why do you think it somehow related to BST? – Alexei Levenkov Mar 21 '18 at 02:51
  • @AlexeiLevenkov :) Thanks for pointing out that. I did some search, .NET version of HashSet is just based on linear algorithms. In comparison, Java 8's HashMap is using self-balancing BST. – detale Mar 22 '18 at 18:34
2

Generally, no.

Yes, there are cases where the list is in an Array or List, but that's not the general case.

Steven Sudit
  • 19,391
  • 1
  • 51
  • 53
2

An extensionmethod like this would do it...

public static bool In<T>(this T item, params T[] items)
{
    return items.Contains(item);
}

Use it like this:

Console.WriteLine(1.In(1,2,3));
Console.WriteLine("a".In("a", "b"));
Allrameest
  • 4,364
  • 2
  • 34
  • 50
2

You can use the switch statement with pattern matching (another version of jules's answer):

if (value switch{1 or 3 => true,_ => false}){
  // do something
}
Sardelka
  • 354
  • 3
  • 9
1

Using Extension Methods:

public static class ObjectExtension
{
    public static bool In(this object obj, params object[] objects)
    {
        if (objects == null || obj == null)
            return false;
        object found = objects.FirstOrDefault(o => o.GetType().Equals(obj.GetType()) && o.Equals(obj));
        return (found != null);
    }
}

Now you can do this:

string role= "Admin";
if (role.In("Admin", "Director"))
{ 
    ...
} 
Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
goenning
  • 6,514
  • 1
  • 35
  • 42
1

Easier is subjective, but maybe the switch statement would be easier? You don't have to repeat the variable, so more values can fit on the line, and a line with many comparisons is more legible than the counterpart using the if statement.

Tim
  • 5,371
  • 3
  • 32
  • 41
1

In vb.net or C# I would expect that the fastest general approach to compare a variable against any reasonable number of separately-named objects (as opposed to e.g. all the things in a collection) will be to simply compare each object against the comparand much as you have done. It is certainly possible to create an instance of a collection and see if it contains the object, and doing so may be more expressive than comparing the object against all items individually, but unless one uses a construct which the compiler can explicitly recognize, such code will almost certainly be much slower than simply doing the individual comparisons. I wouldn't worry about speed if the code will by its nature run at most a few hundred times per second, but I'd be wary of the code being repurposed to something that's run much more often than originally intended.

An alternative approach, if a variable is something like an enumeration type, is to choose power-of-two enumeration values to permit the use of bitmasks. If the enumeration type has 32 or fewer valid values (e.g. starting Harry=1, Ron=2, Hermione=4, Ginny=8, Neville=16) one could store them in an integer and check for multiple bits at once in a single operation ((if ((thisOne & (Harry | Ron | Neville | Beatrix)) != 0) /* Do something */. This will allow for fast code, but is limited to enumerations with a small number of values.

A somewhat more powerful approach, but one which must be used with care, is to use some bits of the value to indicate attributes of something, while other bits identify the item. For example, bit 30 could indicate that a character is male, bit 29 could indicate friend-of-Harry, etc. while the lower bits distinguish between characters. This approach would allow for adding characters who may or may not be friend-of-Harry, without requiring the code that checks for friend-of-Harry to change. One caveat with doing this is that one must distinguish between enumeration constants that are used to SET an enumeration value, and those used to TEST it. For example, to set a variable to indicate Harry, one might want to set it to 0x60000001, but to see if a variable IS Harry, one should bit-test it with 0x00000001.

One more approach, which may be useful if the total number of possible values is moderate (e.g. 16-16,000 or so) is to have an array of flags associated with each value. One could then code something like "if (((characterAttributes[theCharacter] & chracterAttribute.Male) != 0)". This approach will work best when the number of characters is fairly small. If array is too large, cache misses may slow down the code to the point that testing against a small number of characters individually would be faster.

supercat
  • 77,689
  • 9
  • 166
  • 211
0
public static bool EqualsAny<T>(IEquatable<T> value, params T[] possibleMatches) {
    foreach (T t in possibleMatches) {
        if (value.Equals(t))
            return true;
    }
    return false;
}
public static bool EqualsAny<T>(IEquatable<T> value, IEnumerable<T> possibleMatches) {
    foreach (T t in possibleMatches) {
        if (value.Equals(t))
            return true;
    }
    return false;
}
JWL_
  • 829
  • 5
  • 15
0

I had the same problem but solved it with a switch statement switch(a value you are switching on) { case 1: the code you want to happen; case 2: the code you want to happen; default: return a value }