27

Is there another way to write something like this:

if (a == x || a == y || a == z)

One way that I found is doing it like this:

if( new [] {x,y,z}.Contains(a))

Are there other good ways?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Omu
  • 64,955
  • 87
  • 259
  • 396
  • 2
    Why do you want it in other ways? I'm just curious – David Espart Jul 14 '10 at 08:07
  • @despart - It makes a little more sense with descriptive variable names but in either case I would still use the logical operators. – ChaosPandion Jul 14 '10 at 08:08
  • Probably cause creating an array just to compare more than two numbers feels wrong. – cHao Jul 14 '10 at 08:08
  • @despart: substitute `a` with a longer expression that you only want to calculate once. also imagine it's used inside a lambda where you would prefer to not have to introduce a variable, because lambdas are neater in their one-line syntax. in that case the first version cannot be used, and the second, if you ask me, is just hard on the eyes – David Hedlund Jul 14 '10 at 08:10
  • @despart because it would be much less to write – Omu Jul 14 '10 at 08:10
  • This has been asked several times before, but as usual my search skills have let me down. – ChrisF Jul 14 '10 at 08:40
  • Related to: http://stackoverflow.com/questions/3205065/why-do-most-programming-languages-only-have-binary-equality-comparison-operators – Babar Jul 14 '10 at 11:47

11 Answers11

64

I often use an extension method that mimics SQLs IN:

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

That way I can do

if(a.IsIn(b, c, d)) { ... }
David Hedlund
  • 121,697
  • 28
  • 196
  • 213
  • 2
    It should be noted that this is doing the exact same thing as in your second example, but it tucks the act of arranging it in an array and searching in it out of sight, so that you're left with straightforward readable operations. – David Hedlund Jul 14 '10 at 08:12
  • @David Hedlund, ya I noticed that, I was just thinking that probably c# has something for this – Omu Jul 14 '10 at 08:14
  • @Omu: it's not available out of the box, no. This discussion springs to mind: http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx – David Hedlund Jul 14 '10 at 08:16
  • 4
    shouldn't it return a boolean? – Tommy Andersen Jul 14 '10 at 08:20
  • 3
    Using `params` for this is genius. +1! – tzaman Jul 14 '10 at 08:21
  • @TommyA: of course =) i guess i was too quick on the keyboard there. good catch – David Hedlund Jul 14 '10 at 08:28
  • @David Hedlund: What is a is null? – Gertjan Jul 14 '10 at 08:38
  • 2
    @Gertjan: if `a` is null, it will return *true* if any other items in the set are null, but *false* if no other items in the set are null. it should be noted that you can write `string a = null; a.IsIn("a","b")` but you *cannot* write `null.IsIn("a","b")` because in that case the type cannot be inferred. but that would be nonsensical to write, anyway... – David Hedlund Jul 14 '10 at 08:49
  • @David Hedlund: Ah thank you. Looking at your code again is see it is a static. I would have expected it to go for a null pointer exception in case a == null since I expected the function to not be there. But looking at it makes sense! Thank you. – Gertjan Jul 14 '10 at 10:04
  • @Gertjan: to be honest, the `static` is an edit i made, possibly after your question, but I did mention (and denote with `this`) that it was an *extension* method, so the `static` was implied all along, although as it was, without it, it wouldn't have compiled – David Hedlund Jul 14 '10 at 10:21
  • 1
    To get this to work I had to implement it in a non generic static class and changed *collection.Contains(obj);* to *new ArrayList(collection).Contains(obj);* as System.Array does not seem to have a contains method. But all in all, brilliant work :-) +1 – kamui Dec 14 '11 at 10:59
  • 1
    @kamui: Extension methods have to be in static non-generic classes, yes, but the method itself may still be generic. Strictly, it is `System.Linq.Enumerable.Contains()` that is being used, which is available on `T[]`. You would need to include `using System.Linq;` – David Hedlund Dec 14 '11 at 11:23
12

You have the classic switch statement :

switch(a) {
    case x:
    case y:
    case z:
        // Do stuff
        break;
}
Mongus Pong
  • 10,721
  • 9
  • 40
  • 72
6

Just for fun:

using System;

static class Program {

    static bool In(this object obj, params object[] values) {
        foreach (object value in values) {
            if (obj.Equals(value)) {
                return true;
            }
        }
        return false;
    }

    static void Main(string[] args) {
        bool test1 = 3.In(1, 2, 3);
        bool test2 = 5.In(1, 2, 3);
    }
}

But I really think that the best way is to write the plain check

if(a == x || a == y || a == z)

As everybody will understand immediately what it does.

Paolo Tedesco
  • 49,782
  • 29
  • 130
  • 181
  • I went for this solution as well (see my answer), although I strongly recommend you make that generic, to avoid boxing/unboxing. the invocation will still be exactly the same, you don't have to explicitly specify the type parameter, as it will be inferred from usage... – David Hedlund Jul 14 '10 at 08:14
5

Your solution to rewrite it as

if( new [] {x,y,z}.Contains(a))

is not a good move.

You've take a simple efficient logical operation, which every programmer easily understands and which contains short-circuiting logic to speed it up and instead you've produced code that requires a moment to understand and which is considerably less efficient.

Sometimes your fellow engineers will prefer it if you don't try to be "clever"!

GrahamS
  • 9,095
  • 7
  • 45
  • 61
  • I do agree that it would be slower, but `Contains` would still short-circuit. Well at least any reasonable implementation would that is. – Brian Gideon Jul 14 '10 at 13:09
  • @Brian Gideon: `Contains` will short-circuit, sure, so it won't continue with the comparisons if a match is already found, but before `Contains` can start, you need to initialize the array, and it is at that point that you access `x`, `y` and `z`. if `z` requires heavy computation to evaluate, and `a == x`, `z` will still be evaluated before the `Contains` operation is even started. – David Hedlund Jul 14 '10 at 13:41
  • To be honest the relative efficiency isn't really a major issue on any modern system (excepted possibly an embedded environment). The real issue is the mental stumbling block that odd code like this introduces for other engineers. – GrahamS Jul 14 '10 at 14:31
5

Consider a case where a == x, and y and z are slow-to-evaluate, expensive functions.

  • In if(a == x || a == y || a == z) you have the benefit of the short-circuit ||-operator, so you y and z won't be evaluated.
  • If you make an array with new[] { x, y, z } - y and z will be evaluated every time.

The 'trick' with .Contains() would be more useful if there was an elegant syntax to create lazy-evaluated sequence (IEnumerable<T>). i.e. something like yield return x; yield return y;..., but inlined and shorter.

Danko Durbić
  • 6,637
  • 5
  • 32
  • 37
3

So, you want to replace a simple, efficent language construct that contains short-circuit optimisations into something much slower that has the potential for throwing exceptions?

However, if the items you want to compare against are not fixed in quantity, i.e. at run time it could be t,u,v,w,x,y,z,etc..., then the Collection.Contains method is the only option, but then you'd be passing collection objects around rather than individual values and so there's little memory allocation ovrehead.

If you've got a large number of items to compare 'a' against, but the items are not dynamic at run time then a switch statement might be a better fit.

Skizz
  • 64,439
  • 10
  • 63
  • 105
2

Why would you need yet another way? Since it isn't a matter of functionality, I would guess the point is to improve readability. If you have a few variables with meaningful names, it would be more readable to just compare by using ==. If you have more, you can use Contains against a list as in your other sample. Yet another way would be comparing against enum flags:

[Flags]
public enum Size
{
    Small = 1,
    Medium = 2,
    Large = 4
}

And then to find out if mySize is in Small or Medium:

selectedSizes = Size.Small | Size.Medium;
mySize = Size.Small;
if (mySize & selectedSizes)
{
  ... 
}
Yakimych
  • 16,939
  • 7
  • 46
  • 66
2

Fun fact, as of C#9 this is possible

var c ='b';

if (c is 'a' or 'b' or 'c')
  Console.WriteLine("yes");

Which compiles to

if (c == 'a' || c == 'b' || c == 'c')
{
   Console.WriteLine("yes");
}

Or you can get more creative

if (c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or '.' or ',')
   Console.WriteLine("yes");

Which would roughly compile to (according to sharp io)

if (c >= 'a')
{
   if (c <= 'z')
   {
      goto IL_0025;
   }
}
else if (c >= 'A')
{
   if (c <= 'Z')
   {
      goto IL_0025;
   }
}
else if (c == ',' || c == '.')
{
   goto IL_0025;
}
bool flag = false;
goto IL_002b;
IL_0025:
flag = true;
goto IL_002b;
IL_002b:
if (flag)
{
   Console.WriteLine("yes");
}

Or use it in a switch

switch (c)
{
   case 'a' or 'b' or 'c':
      Console.WriteLine("yes");
   break;
}
TheGeneral
  • 69,477
  • 8
  • 65
  • 107
1
if(a==x?true:a==y?true:a==z?true:false)
didxga
  • 5,316
  • 4
  • 39
  • 53
  • 1
    ooooh interesting, but nowhere near as readable as if(a == x || a == y || a == z) ! – RYFN Jul 14 '10 at 08:16
1

Try this

var res2 = new[] { 1, 2, 3 }.Any(x => x == 2);
RameshVel
  • 60,384
  • 28
  • 166
  • 207
0

For instance, your logic is like that:

if(a==x || a== y|| a==z)
{
    DoSomething();
} 
else
{
   DoOtherThings();
}

will equivalent to:

if(a!=x&& a != y && a!= z)
{
   DoOtherThings();
}
else
{
   DoSomething();
}

Cheers.

Toan Nguyen
  • 10,285
  • 5
  • 37
  • 56
  • 1
    :) that was not the point, You did the same but just a different form, you had to write "a" just once – Omu Jul 14 '10 at 11:01