引用值类型的相等性

ame*_*gan 28 c# boxing ref

我做了一些ref关键字测试,有一个认为我无法理解:

static void Test(ref int a, ref int b)
{
    Console.WriteLine(Int32.ReferenceEquals(a,b));
}

static void Main(string[] args)
{
    int a = 4;
    Test(ref a, ref a);
    Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)

为什么显示此代码False?我知道这int是一个值类型,但在这里它应该传递对同一个对象的引用.

Yuv*_*kov 38

为什么显示此代码False

因为int a并且在你打电话时int b 被装箱object.ReferenceEquals.每个整数都装在一个object实例中.因此,您实际上是在比较两个盒装值之间的参考,这两个值明显不相等.

如果查看方法生成的CIL,您可以很容易地看到这个:

Test:
IL_0000:  nop
IL_0001:  ldarg.0     Load argument a
IL_0002:  ldind.i4
IL_0003:  box         System.Int32
IL_0008:  ldarg.1     Load argument b
IL_0009:  ldind.i4
IL_000A:  box         System.Int32
IL_000F:  call        System.Object.ReferenceEquals
IL_0014:  call        System.Console.WriteLine
IL_0019:  nop
IL_001A:  ret
Run Code Online (Sandbox Code Playgroud)

通过使用可验证的CIL(例如@ leppie的答案)或unsafe代码,可以检查存储位置是否相等:

unsafe static void Main(string[] args)
{
    int a = 4;
    int b = 5;
    Console.WriteLine(Test(ref a, ref a)); // True
    Console.WriteLine(Test(ref a, ref b)); // False;
}

unsafe static bool Test(ref int a, ref int b)
{
    fixed (int* refA = &a)
    fixed (int* refB = &b)
    {
        return refA == refB;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @ M.kazemAkhgary是的,如果你直接操纵`a`它会改变它的价值.这(OP看到的行为)的发生是因为在调用`ReferenceEquals`时,两个值都被装箱(仅用于方法调用). (2认同)

lep*_*pie 18

这不能直接在C#中完成.

但是,您可以在可验证的CIL中实现它:

.method public hidebysig static bool Test<T>(!!T& a, !!T& b) cil managed
{
  .maxstack 8
  ldarg.0 
  ldarg.1 
  ceq 
  ret 
}
Run Code Online (Sandbox Code Playgroud)

测试

int a = 4, b = 4, c = 5;
int* aa = &a; // unsafe needed for this
object o = a, p = o;
Console.WriteLine(Test(ref a, ref a)); // True
Console.WriteLine(Test(ref o, ref o)); // True
Console.WriteLine(Test(ref o, ref p)); // False
Console.WriteLine(Test(ref a, ref b)); // False
Console.WriteLine(Test(ref a, ref c)); // False
Console.WriteLine(Test(ref a, ref *aa)); // True
// all of the above works for fields, parameters and locals
Run Code Online (Sandbox Code Playgroud)

笔记

这实际上并没有检查相同的引用,但更精细,因为它确保两者都是相同的"位置"(或从同一个变量引用).这是第三行返回false即使o == p返回true.然而,这种"位置"测试的有用性非常有限.