11

最近我从 VB 转到 C#,所以我经常使用 C# 到 VB.NET 的转换器来了解语法差异。在将下一个方法转移到 VB 时,我注意到一件有趣的事情。

C#原代码:

 public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools)
{
   int trueCnt = 0;
   foreach(bool b in bools)
      if (b && (++trueCnt > threshold)) 
          return true;
   return false;          
} 

VB.NET 结果:

Public Function ExceedsThreshold(threshold As Integer, bools As IEnumerable(Of Boolean)) As Boolean
Dim trueCnt As Integer = 0
For Each b As Boolean In bools
    If b AndAlso (System.Threading.Interlocked.Increment(trueCnt) > threshold) Then
        Return True
    End If
Next
Return False End Function

C#的++操作符被替换为System.Threading.Interlocked.Increment 是否意味着++如果在foreach循环中使用不是线程安全的操作符成为线程安全的?它是一种语法糖吗?如果这是真的,那为什么转换器放在Interlocked.IncrementVB版本中呢?我认为 C# 和 VB 中的 foreach 工作方式完全相同。或者它只是一个转换器“保险”?

4

3 回答 3

12

我敢肯定这只是一个转换器黑客,我想我可以解释这背后的原因。

但首先,为了回答您的问题,C# 中的内置++运算符不是线程安全的。它只是以下过程的语法糖(在 的情况下++i):

  • 读取值i
  • 增加它
  • 写回i
  • 返回增加的值

由于存在单独的读取和写入,因此这是一个非原子操作。

现在,在 VB 中没有直接等效的++运算符。最接近的是:

i += 1

但这是一个声明。相比之下,++i是一个表达式。您可以++i在另一个语句或表达式中使用,但不能在 VB 语句中使用。

使用Interlocked.Increment只是一种轻松翻译代码的聪明方法,而不必将整个语句分解为多个其他语句。

如果没有这个技巧,转换器将不得不像这样分解表达式:

if (b && (++trueCnt > threshold))
    ...
If b Then
    trueCnt += 1
    If trueCnt > threshold Then
        ...
    End If
End If

如您所见,这需要更多的重写。如果是一个属性,它甚至需要引入一个单独的临时变量trueCnt(以避免对其进行两次评估)。

与转换器使用的更简单的句法转换相比,这需要更深入的语义trueCnt += 1分析和控制流重写——因为不能VB 的表达式中使用。

于 2015-04-04T18:03:47.350 回答
4

我相信这是因为您希望在进行比较的同一语句中增加值。我对此没有太多的理由,但确实感觉像是正确的答案,因为 trueCnt += 1 不允许在同一行进行比较。它当然与它是一个 foreach 无关,尝试在循环外添加相同的行,我几乎可以肯定它也会转换为 Increment。VB .Net 中根本没有其他语法可以在同一行中同时递增和比较。

于 2015-04-04T18:02:09.103 回答
2

除了上述问题之外,C# 的默认行为遵循 Java 不幸的整数溢出行为。尽管有时包装整数溢出语义很有用,但 C# 通常不会区分整数应该在溢出时包装与整数不应该包装但程序员认为溢出陷阱不值得的情况。这可能会混淆转换工作,因为 VB.NET 使捕获行为比包装行为更容易和更快,而 C# 则相反。因此,翻译出于速度原因使用未检查数学的代码的逻辑方法是在 VB.NET 中使用普通检查数学,而翻译需要包装行为的代码的逻辑方法是在 VB.NET 中使用包装整数方法.Threading.Interlocked.IncrementThreading.Increment.Add方法使用包装整数行为,因此虽然从速度的角度来看它们不是最佳的,但它们很方便。

于 2015-04-05T00:20:23.037 回答