C#. 如果 (a == (b 或 c 或 d)),可以吗?

28

有没有其他的写法可以像这样:

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

我发现一种方法就是像这样做:

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

还有其他好的方法吗?


2
你为什么想要其他的方式?我只是好奇。 - David Espart
@despart - 如果使用描述性变量名称会更有意义一些,但无论如何我仍然会使用逻辑运算符。 - ChaosPandion
创建一个数组来比较两个以上的数字可能会让人感到不太对劲。 - cHao
@despart:用一个更长的表达式替换 a,这样你只需要计算一次。同时想象一下它被用在一个 lambda 内部,在那里你不想引入变量,因为 lambda 的单行语法更加简洁。在这种情况下第一个版本不能使用,如果你问我,第二个版本只会让人眼花缭乱。 - David Hedlund
@despart 因为这样写起来会少得多。 - Omu
显示剩余2条评论
11个回答

64

我经常使用一个扩展方法来模拟SQL中的IN

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

那样我就可以做到

if(a.IsIn(b, c, d)) { ... }

2
需要注意的是,这与您第二个示例中所做的完全相同,但它将排列在数组中并在其中搜索的操作隐藏起来,因此您只剩下直观易读的操作。 - David Hedlund
4
它不应该返回一个布尔值吗? - Tommy Andersen
3
使用 params 真是太聪明了,点赞! - tzaman
2
@Gertjan:如果a为空,它将返回true,如果集合中的任何其他项为空,则返回true,但如果集合中没有其他项为空,则返回false。需要注意的是,您可以编写string a = null; a.IsIn("a","b"),但您不能编写null.IsIn("a","b"),因为在这种情况下无法推断类型。但是,无论如何编写都是荒谬的... - David Hedlund
1
@kamui:扩展方法必须在静态非泛型类中,是的,但方法本身仍然可以是泛型的。严格来说,使用的是System.Linq.Enumerable.Contains<T>(),它可用于T[]。您需要包含using System.Linq; - David Hedlund
显示剩余7条评论

12

你有经典的 switch 语句:

switch(a) {
    case x:
    case y:
    case z:
        // Do stuff
        break;
}

同意;这绝对是最好的方法(至少,是我采取的方式)。 - Noon Silk
2
如果x、y和z不是常量,C#会出现问题吗? - cHao
@cHao,那样做没问题!只要这些变量在之前有受影响的值,a就会与x、y和z的内存值进行比较。 - SiN
2
@SiN:什么?不是的。switch语句要求每个case都是编译时常量。 - David Hedlund
嗯...我认为C#不允许在case之间穿透。 - Ray Hidayat
2
@Ray,这并不意味着你可以在 case x: 后面放置一些代码,并允许它流到 case y:。只要在案例之间没有代码,就可以了。 - Mongus Pong

6

仅供娱乐:

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);
    }
}

但我真的认为最好的方法是撰写普通支票。

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

每个人都能立刻理解它的作用。


我也选择了这个解决方案(请参见我的答案),尽管我强烈建议您将其设置为通用类型,以避免装箱/拆箱。调用仍将完全相同,您不必明确指定类型参数,因为它将从使用中推断出来... - David Hedlund

6

您将其改写为的解决方案

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

这不是一个好的做法。

你把一个简单有效的逻辑操作转化为了需要一段时间才能理解且效率明显降低的代码。这个逻辑操作中包含了短路逻辑来提高效率,而你却没有保留它。

有时候,你的同事们会更喜欢你不要试图“聪明”!


我确实同意这会变慢,但是Contains仍然会短路。至少任何合理的实现都应该是这样的。 - Brian Gideon
@Brian Gideon:Contains会进行短路操作,确保在找到匹配项后不会继续比较,但在Contains开始之前,您需要初始化数组,并且此时访问xyz。如果z需要进行大量计算才能评估,并且a == x,则在开始Contains操作之前仍将评估z - David Hedlund
说实话,在现代系统中相对效率并不是一个主要问题(可能除了嵌入式环境)。真正的问题是,这样奇怪的代码会给其他工程师带来心理障碍。 - GrahamS

5
考虑一种情况,其中a == x,而yz是慢速计算的昂贵函数。
  • if(a == x || a == y || a == z)中,您可以利用短路||运算符的优势,这样yz就不会被计算。
  • 如果使用new[] { x, y, z }创建数组,则每次都会计算yz

.Contains()的“技巧”将更加实用,如果有一种优雅的语法来创建惰性求值序列(IEnumerable<T>),即类似于yield return x; yield return y;...但是内联和更短。


4

有趣的事实,自C#9起就可以实现这一点

var c ='b';

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

编译成

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

或者你可以更有创意一些

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

这段代码编译后大致会变成(在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");
}

或者在switch语句中使用它

switch (c)
{
   case 'a' or 'b' or 'c':
      Console.WriteLine("yes");
   break;
}

3

所以,您想要替换一个包含短路优化的简单高效语言结构,改用一个潜在抛出异常的更慢的东西吗?

然而,如果您要比较的项目数量不是固定的,即在运行时可能是t、u、v、w、x、y、z等等,则Collection.Contains方法是唯一的选择,但这样您将传递集合对象而不是单个值,因此几乎没有内存分配开销。

如果您有大量要与'a'进行比较的项目,但这些项目在运行时不是动态的,则switch语句可能更适合。


当处理动态比较数量时,我喜欢使用HashSet<T> - ChaosPandion

2

为什么需要另一种方式?既然这不是功能问题,我猜测重点在于提高可读性。 如果您有一些具有有意义的名称的变量,那么使用 == 进行比较会更易读。如果您有更多的变量,可以像其他示例中一样使用 Contains 对列表进行比较。 另一种方法是与枚举标志进行比较:

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

接下来要找出mySize是否属于SmallMedium:

selectedSizes = Size.Small | Size.Medium;
mySize = Size.Small;
if (mySize & selectedSizes)
{
  ... 
}

1

试试这个

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

0
if(a==x?true:a==y?true:a==z?true:false)

1
哦,有趣,但不及 if(a == x || a == y || a == z) 那么易读。 - RYFN

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接