2015-07-07 8 views
28

Я сделал несколько тестов 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(); 
} 

Почему этот код отображения False? Я знаю, что int - это тип значения, но здесь он должен передавать ссылки на один и тот же объект.

+0

потому что ссылки не похожи для типов значений. –

+1

Модификатор 'ref' не вызывает привязку соответствующего аргумента к ссылочному типу. – Lee

+0

Вы пытаетесь увидеть, являются ли эти два параметра ссылками на одну и ту же переменную? – BoltClock

ответ

38

Зачем нужен этот код False?

Поскольку int a и int bare being boxed при вызове 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 

Проверка на месте хранения равенства может быть достигнуто либо путем использования проверяемый КСС (например, в @leppie's answer) или 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; 
    } 
} 
+1

Что произойдет, если вы измените один из них внутри метода? я думаю, что это невозможно –

+0

@ M.kazemAkhgary Я не понял ваш вопрос. –

+0

Что произойдет, если вы измените 'a' или' b' внутри 'Test'. Я имею в виду, что они оба ссылаются на одно и то же ('a' в Main). я имею в виду, если вы измените один из них, другой тоже изменится? –

18

Это не может быть сделано непосредственно в C#.

Однако вы можете реализовать его в проверяемом КССЕ:

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

Тесты

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 

Примечание

Это на самом деле не проверять же ссылки, но еще мелкозернистый, поскольку он обеспечивает одинаковое «местоположение» (или ссылку из той же переменной) тоже. Это пока 3-я строка возвращает false, хотя o == p возвращает true. Однако полезность этого теста «местоположения» очень ограничена.

+0

Примечание: это не значит, что это ответ, просто окольный способ его достижения. – leppie

+0

Отлично. Я создал ответ «C#» для этого, используя «DynamicMethod» [здесь] (http://stackoverflow.com/a/43570334/147511). Большое спасибо. –

2

Я знаю, что ИНТ является типом значения, но здесь она должна передавать ссылки на тот же объект.

Да, ссылка передается методу являются одинаковыми, но они зажаты (в пересчете на объект/ссылочный тип) в методе ReferenceEquals.

Вот почему результат вашего теста возвращает false, так как вы сравниваете ссылки двух разных объектов, из-за бокса.

См: Object.ReferenceEquals Method

При сравнении типов значений.Если objA и objB - типы значений, , они помещаются в коробку, прежде чем они будут переданы в ReferenceEquals метод. Это означает, что если оба objA и objB представляют экземпляр же типа значения, то ReferenceEquals метод, тем не менее возвращает ложное

0

Путаница здесь потому, что в отличие от указателей (как в *), «реф» в C# не является частью типа, а является частью сигнатуры метода. Он применяется к параметру и означает, что «это нельзя копировать». Это не означает, что «этот аргумент имеет ссылочный тип».

Параметр, переданный ref, вместо представления нового места хранения, является псевдонимом некоторого существующего местоположения. Как создается псевдоним, технически является детальностью реализации. Чаще всего псевдонимы реализуются как управляемые ссылки, но не всегда. В некоторых связанных с асинхронными случаями, например, ссылка на элемент массива может быть внутренне представлена ​​как комбинация массива и индекса.

По существу для всех целей ваши a и b все еще понимаются C# в качестве типизированных переменных. Это законно и совершенно нормально использовать их в любом выражении, которое принимает значения int, такие как a + b или SomeMethod (a, b), и в этих случаях используются фактические значения int, хранящиеся в a и b.

На самом деле нет понятия «ссылки» как объекта, с которым вы можете напрямую работать с C#. В отличие от указателей, предполагается, что фактические значения управляемых ссылок могут быть изменены в любой момент или даже асинхронно с помощью GC, поэтому набор значимых сценариев для управляемых ссылок будет крайне ограниченным.

 Смежные вопросы

  • Нет связанных вопросов^_^