2009-05-05 87 views
4

我能达到的OR运算符在C#

if (a == "b" || "c") 

代替

if (a == "b" || a== "c") 

+0

您确定要使用|运算符(二进制或)而不是|| (逻辑或)? – 2009-05-05 17:42:17

+0

...你不是说||运营商? – 2009-05-05 17:42:36

+0

正确!我编辑了这篇文章。 – 2009-05-05 17:47:33

回答

14

不,你可以这样做:

if (new[] { "b", "c" }.Contains(a)) 

如果你有LINQ扩展可用,但几乎没有改善。


在回答有关性能的评论,这里的一些基本的计时码。请注意,代码必须以批判的眼光来看待,我可能在这里做了一些使时间偏离的事情。

结果第一:

||, not found: 26 ms 
||, found: 8 ms 
array.Contains, not found: 1407 ms 
array.Contains, found: 1388 ms 
array.Contains, inline array, not found: 1456 ms 
array.Contains, inline array, found: 1427 ms 
switch-statement, not interned, not found: 26 ms 
switch-statement, not interned, found: 14 ms 
switch-statement, interned, not found: 25 ms 
switch-statement, interned, found: 8 ms 

所有的代码被执行两次,也只有通过NR。据报告2,以消除等式中的JITting开销。两个遍都执行了每种类型的检查一百万次,并且在要找到的元素是找到它的元素之一(即,if语句将执行其块)以及一旦元素不是(该块不会执行)。报告每个的时间。我测试了预构建数组和每次构建的数组,这部分我不确定编译器推导并优化了多少,这里可能存在缺陷。

在任何情况下,使用switch-statement(无论是否首先实际使用字符串)都会给出与简单或声明大致相同的结果,这是预期的结果,而数组查找很多更昂贵,这对我来说也是预料之中的。

请修改代码,并纠正(或评论)它,如果有问题。

而这里的源代码,而长:

using System; 
using System.Linq; 
using System.Diagnostics; 
namespace StackOverflow826081 
{ 
    class Program 
    { 
     private const Int32 ITERATIONS = 1000000; 
     static void Main() 
     { 
      String a; 
      String[] ops = CreateArray(); 
      Int32 count; 
      Stopwatch sw = new Stopwatch(); 
      Int32 pass = 0; 
      Action<String, Int32> report = delegate(String title, Int32 i) 
      { 
       if (pass == 2) 
        Console.Out.WriteLine(title + ": " + sw.ElapsedMilliseconds + " ms"); 
      }; 

      for (pass = 1; pass <= 2; pass++) 
      { 
       #region || operator 

       a = "a"; 
       sw.Start(); 

       count = 0; 
       for (Int32 index = 0; index < ITERATIONS; index++) 
       { 
        if (a == "b" || a == "c") 
        { 
         count++; 
        } 
       } 
       sw.Stop(); 
       report("||, not found", count); 
       sw.Reset(); 

       a = "b"; 
       sw.Start(); 

       count = 0; 
       for (Int32 index = 0; index < ITERATIONS; index++) 
       { 
        if (a == "b" || a == "c") 
        { 
         count++; 
        } 
       } 
       sw.Stop(); 
       report("||, found", count); 
       sw.Reset(); 

       #endregion 

       #region array.Contains 

       a = "a"; 
       sw.Start(); 

       count = 0; 
       for (Int32 index = 0; index < ITERATIONS; index++) 
       { 
        if (ops.Contains(a)) 
        { 
         count++; 
        } 
       } 
       sw.Stop(); 
       report("array.Contains, not found", count); 
       sw.Reset(); 

       a = "b"; 
       sw.Start(); 

       count = 0; 
       for (Int32 index = 0; index < ITERATIONS; index++) 
       { 
        if (ops.Contains(a)) 
        { 
         count++; 
        } 
       } 
       sw.Stop(); 
       report("array.Contains, found", count); 
       sw.Reset(); 

       #endregion   

       #region array.Contains 

       a = "a"; 
       sw.Start(); 

       count = 0; 
       for (Int32 index = 0; index < ITERATIONS; index++) 
       { 
        if (CreateArray().Contains(a)) 
        { 
         count++; 
        } 
       } 
       sw.Stop(); 
       report("array.Contains, inline array, not found", count); 
       sw.Reset(); 

       a = "b"; 
       sw.Start(); 

       count = 0; 
       for (Int32 index = 0; index < ITERATIONS; index++) 
       { 
        if (CreateArray().Contains(a)) 
        { 
         count++; 
        } 
       } 
       sw.Stop(); 
       report("array.Contains, inline array, found", count); 
       sw.Reset(); 

       #endregion 

       #region switch-statement 

       a = GetString().Substring(0, 1); // avoid interned string 
       sw.Start(); 

       count = 0; 
       for (Int32 index = 0; index < ITERATIONS; index++) 
       { 
        switch (a) 
        { 
         case "b": 
         case "c": 
          count++; 
          break; 
        } 
       } 
       sw.Stop(); 
       report("switch-statement, not interned, not found", count); 
       sw.Reset(); 

       a = GetString().Substring(1, 1); // avoid interned string 
       sw.Start(); 

       count = 0; 
       for (Int32 index = 0; index < ITERATIONS; index++) 
       { 
        switch (a) 
        { 
         case "b": 
         case "c": 
          count++; 
          break; 
        } 
       } 
       sw.Stop(); 
       report("switch-statement, not interned, found", count); 
       sw.Reset(); 

       #endregion      

       #region switch-statement 

       a = "a"; 
       sw.Start(); 

       count = 0; 
       for (Int32 index = 0; index < ITERATIONS; index++) 
       { 
        switch (a) 
        { 
         case "b": 
         case "c": 
          count++; 
          break; 
        } 
       } 
       sw.Stop(); 
       report("switch-statement, interned, not found", count); 
       sw.Reset(); 

       a = "b"; 
       sw.Start(); 

       count = 0; 
       for (Int32 index = 0; index < ITERATIONS; index++) 
       { 
        switch (a) 
        { 
         case "b": 
         case "c": 
          count++; 
          break; 
        } 
       } 
       sw.Stop(); 
       report("switch-statement, interned, found", count); 
       sw.Reset(); 

       #endregion 
      } 
     } 

     private static String GetString() 
     { 
      return "ab"; 
     } 

     private static String[] CreateArray() 
     { 
      return new String[] { "b", "c" }; 
     } 
    } 
} 
+0

如果数组是静态分配的,那么性能测试可能会很有趣(如果表达式花费的时间足够长以便根据时间开销进行测量)。 – Richard 2009-05-05 19:46:24

3

据我所知,这是不是一种选择。

16

那么,最接近你可以得到的是:

switch (a) { 
    case "b": 
    case "c": 
     // variable a is either "b" or "c" 
     break; 
} 
+0

接受的答案是少代码,但使用switch语句的开销较少,我相信。 – 2009-05-05 17:49:50

+0

我推荐此解决方案。首先,它更具可读性。其次,它的开销较小。第三,如果逻辑必须在未来发生变化,则维护起来更容易。 – 2009-05-05 17:53:18

+0

接受的答案可能适合单行,但这个不会为数组分配额外的内存。 – Kon 2009-05-05 18:35:20

2

不,不与语法。但是有很多选项可以编码。

if ("bc".Contains(a)) { } // Maybe check a.Length == 1, too. 

if ((a[0] & 0x62) == 0x62) { } // Maybe check a.Length == 1, too. 

if (new String[] { "b", "c" }.Contains(a)) { } 

也许你可以做一些运算符重载,让你的语法的工作,但是这真的取决于你想要达到什么目的,难以从简单的例子来告诉。

0

不,这不是如何运算符(||)如何在C#中工作。

另一种解决方案,但它使代码的可读性,是创建检查你想要的值的函数,类似于:

public static bool Any(object a, params object[] b) 
{ 
    foreach(object item in b) 
    { 
     if(a == b) 
     { 
      return true; 
     } 
    } 
    return false; 
} 
3

您可以使用正则表达式:

if(Regex.IsMatch(a, "b|c")) 

如果“A”可能比一个字符使用这种较长的内容:

if(Regex.IsMatch(a, "^(b|c)$")) 
2

ÿ你可以在某些情况下。也就是说,标记枚举:

[Flags] 
enum MyEnum { 
    None = 0, 
    A = 1, 
    B = 2, 
    C = 4, 
    D = 8 
} 

//... 

MyEnum a = MyEnum.B 

if((a & (MyEnum.B | MyEnum.C)) > 0) 
    // do something 

等同于:

if((a & MyEnum.B) > 0 || (a & MyEnum.C) > 0) 
    // do something 

这样做的原因有位掩码做。在二进制中,

None = 00000 
A = 00001 
B = 00010 
C = 00100 
D = 01000 

所以当我们使用|运算符,我们进行一点一点的比较,查找列中的任意1并将它们复制到结果中。如果没有1的列,你复制一个0

B 00010 
& C 00100 
--------- 
    00110 

然后当我们应用&经营者,我们复制一个1

(B & C) 00110 
& (a = B) 00010 
--------------- 
      00010 
前1个在每列中的所有行

这是> 0,因此返回true。

奇怪的是,这是最有效的方法,因为它为您节省了一个数值比较(>)和一个逻辑运算符(||),它可以完成所有那些奇怪的短路和什么。