tags:

views:

677

answers:

10

is there another way to write something like this:

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

one way that I found is doing like this:

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

anybody knows any other good ways ?

+46  A: 

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
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
You've been too fast! (+1)
Paolo Tedesco
@David Hedlund, ya I noticed that, I was just thinking that probably c# has something for this
Omu
@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
shouldn't it return a boolean?
TommyA
Using `params` for this is genius. +1!
tzaman
@TommyA you're right, I've edited the answer
Omu
@TommyA: of course =) i guess i was too quick on the keyboard there. good catch
David Hedlund
@David Hedlund: What is a is null?
Gertjan
@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
@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
@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
+6  A: 

You have the classic switch statement :

switch(a) {
    case x:
    case y:
    case z:
        // Do stuff
        break;
}
Mongus Pong
Agreed; this is by far the best way to do this (at least, it's the approach I take).
Noon Silk
Will c# freak out if x, y, and z aren't constants?
cHao
@cHao, no that would work fine! As long as those variables have an affected value earlier on, a is compared to memory values of x,y and z.
SiN
@SiN: huh? no. the switch requires that every case is compile-time constant
David Hedlund
No.. x, y and z have to be consts. It will only work with that scenario..
Mongus Pong
@SiN: http://imgur.com/tfePa.png
David Hedlund
Hm... I was under the impression that C# didn't allow fall-throughs between cases
Ray Hidayat
@Ray, it doesn't in that you could put some code after the case x: and allow it to fall through to the case y:. As long as there is no code between the cases, its fine.
Mongus Pong
@Mongus I see, I didn't know that, cool!
Ray Hidayat
+4  A: 

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
@Paolo Tedesco: 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
@David Hedlund: good point!
Paolo Tedesco
+1 haven't seen this pattern of code... :)
deostroll
A: 
if(a==x?true:a==y?true:a==z?true:false)
didxga
ooooh interesting, but nowhere near as readable as if(a == x || a == y || a == z) !
Zeus
I am so conflicted on how to vote.
ChaosPandion
+3  A: 

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
`HashSet<T>` is what I like to use when dealing with dynamic amounts of comparisons.
ChaosPandion
+1  A: 

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
+1  A: 

Try this

var res2 = new[] { 1, 2, 3 }.Any(x => x == 2);

Cheers

Ramesh Vel
+2  A: 

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
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
@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
@David: Very good point.
Brian Gideon
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
A: 

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.

DrakeVN
:) that was not the point, You did the same but just a different form, you had to write "a" just once
Omu
Misunderstood !
DrakeVN
+2  A: 

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ć