Interlocked.CompareExchange可以抛出NullReferenceException吗?

时间:2016-06-09 10:12:28

标签: c# .net

来自https://msdn.microsoft.com/en-us/library/bb297966(v=vs.110).aspx

[ComVisibleAttribute(false)]
public static T CompareExchange<T>(
    ref T location1,
    T value,
    T comparand
)
where T : class

NullReferenceException    The address of location1 is a null pointer.

但是当我对location1使用空引用时,我没有收到任何错误:

class A { }
class Program
{
    static void Main(string[] args)
    {
        A dest = null;
        A src = new A();
        // If dest is null, then replace with src.
        Interlocked.CompareExchange(ref dest, src, null);
    }
}

这样做可以吗?是否存在将NullReferenceException放入更高版本的.NET中的危险?

2 个答案:

答案 0 :(得分:4)

这里的文档有误导性,因为location1不能是null,因为在C#中它必须始终引用现有变量。所以,是的,如果location1已经src并且#{1}}已经location1,那么就可以将null(当前包含null)设置为location1 34。

实际上因为NullReferenceException必须是引用,所以不可能在C#中传递空引用,尽管它可以(并且有效)将引用传递给null的东西,所以这个函数永远不会抛出null

我怀疑这取决于从Win32 api移植的文档,您必须将指针传递给读取/写入变量的位置,在此处传递EntryQuery::create()->joinWith('Company')->joinWith("User") 将是错误的。

答案 1 :(得分:1)

正如另一个答案所说,让变量包含null是完全可以的 - 它是关于对变量的引用是null,而不是发生在C#或大多数其他托管语言中。

那就是说,你可以Interlocked.CompareExchange通过直接从IL工作来抛出NullReferenceException。但即使在那里,如果你想保持在可管理的,可验证的代码领域,你也必须变得棘手:

  .method private hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    .maxstack  3
    .locals init ([0] int32& x)  // x is initialized to null
    ldloc.0
    ldc.i4.1
    ldc.i4.2
    call  int32 [mscorlib]System.Threading.Interlocked::CompareExchange(
      int32&, int32, int32)
    pop
    ret
  }

此代码将通过验证,但它会在运行时抛出NullReferenceException。堆栈跟踪实际上不会显示Interlocked.CompareExchange,因为JIT编译器会将其内联到单个lock cmpxchg指令。

正如@Sean正确指出的那样,这不应该证明文档说该方法可能抛出NullReferenceException,因为这种方法可以用来破坏任何函数ref或{{ 1}}参数。例如,如果“out的地址为result”,则Int32.TryParse不会记录它可以抛出NRE,我们也不会指望它。在涉及引用时,隐式期望托管代码表现良好。