Reference equality of value types

后端 未结 4 982
臣服心动
臣服心动 2020-12-30 21:06

I have made some ref keyword tests and there is one think I can\'t understand:

static void Test(ref int a, ref int b)
{
    Console.WriteLine(In         


        
相关标签:
4条回答
  • 2020-12-30 21:42

    I know, that int is a value type but here it should pass references to the same object.

    Yes, the reference passed to the method are the same, but they are boxed (converted to object/reference type) in the ReferenceEquals method.

    That is why the result of your test returns false, since you are comparing references of two different objects, due to boxing.

    See: Object.ReferenceEquals Method

    When comparing value types. If objA and objB are value types, they are boxed before they are passed to the ReferenceEquals method. This means that if both objA and objB represent the same instance of a value type, the ReferenceEquals method nevertheless returns false

    0 讨论(0)
  • 2020-12-30 21:49

    This cannot be done directly in C#.

    You can however implement it in verifiable CIL:

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

    Tests

    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
    

    Notes

    This does not actually check for the same reference, but even more fine-grained in that it makes sure both are the same 'location' (or referenced from the same variable) too. This is while the 3rd line returns false even though o == p returns true. The usefulness of this 'location' test is very limited though.

    0 讨论(0)
  • 2020-12-30 21:51

    The confusion here is because unlike pointers (as in * ), "ref" in C# is not a part of a type, but a part of a method signature. It applies to the parameter and means "this must not be copied". It does not mean "this argument has reference type".

    Parameter passed by ref, instead of representing a new storage location, is instead an alias to some existing location. How alias is created is technically an implementation detail. Most often aliases are implemented as managed references, but not always. In some async related cases, for example, a reference to an array element could be internally represented as a combination of array and index.

    Essentially for all purposes your a and b are still understood by C# as int-typed variables. It is legal and completely normal to use them in any expression that takes int values like a+b, or SomeMethod(a,b) and in those cases the actual int values stored in a and b are used.

    There is really no concept of a "reference" as an entity that you can directly work with in C#. Unlike pointers, the actual values of managed references must be assumed to be able to change at any moment, or even asynchronously, by GC, so the set of meaningful scenarios on managed references would be extremely limited.

    0 讨论(0)
  • 2020-12-30 22:02

    Why does this code display False?

    Because int a and int b are being boxed when you call object.ReferenceEquals. Each integer is boxed inside an object instance. Thus, you are actually comparing references between two boxed values, which clearly aren't equal.

    You can easily see this if you look at the generated CIL for the method:

    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
    

    Checking for storage location equality can be achieved either by using verifiable CIL (such as in @leppie's answer) or by unsafe code:

    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题