2015-07-07 9 views
28

Ich habe einige ref Keyword-Tests gemacht und es ist eine denke ich nicht verstehen kann:Referenzgleichheit Werttypen

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(); 
} 

Warum ist das Code-Anzeige False? Ich weiß, dass int ein Werttyp ist, aber hier sollte es Referenzen auf das gleiche Objekt übergeben.

+0

weil die Referenzen für Werttypen nicht ähnlich sind. –

+1

Der Modifikator 'ref' bewirkt nicht, dass das entsprechende Argument in einen Referenztyp eingereiht wird. – Lee

+0

Versuchen Sie zu sehen, ob die beiden Parameter Verweise auf die gleiche Variable sind? – BoltClock

Antwort

38

Warum zeigt dieser Code False an?

Da int a und int bare being boxed wenn Sie object.ReferenceEquals nennen. Jede ganze Zahl ist in eine object Instanz eingerahmt. Daher vergleichen Sie tatsächlich Referenzen zwischen zwei Boxed-Werten, die eindeutig nicht gleich sind.

Sie können dies leicht sehen, wenn Sie die erzeugte Blick auf CIL für die Methode:

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 

Überprüfung auf Lagerort Gleichheit erreicht werden entweder durch die Verwendung nachprüfbare CIL (wie in @leppie's answer) oder durch 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; 
    } 
} 
+1

Was passiert, wenn Sie eine von ihnen innerhalb der Methode ändern? Ich denke, das ist nicht möglich –

+0

@ M.kazemAkhgary Ich habe deine Frage nicht verstanden. –

+0

Was passiert, wenn Sie 'a' oder' b' in 'Test' ändern? Ich meine, sie verweisen beide auf dasselbe ('a' in Main). Ich meine, wenn du einen von ihnen änderst, ändert sich auch der andere? –

18

Dies kann nicht direkt in C# getan werden.

können Sie jedoch es in nachprüfbaren CIL implementieren:

.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 

Hinweise

Dies ist eigentlich nicht für den gleichen Bezug zu überprüfen, aber noch mehr feinkörnig, indem sichergestellt wird, dass beide den gleichen "Ort" haben (oder referenziert werden) von der gleichen Variable) auch. Dies ist, während die dritte Zeile false zurückgibt, obwohl o == ptrue zurückgibt. Der Nutzen dieses "Standort" -Tests ist jedoch sehr begrenzt.

+0

Hinweis: Dies ist nicht als Antwort gedacht, nur ein Umweg, um es zu erreichen. – leppie

+0

Hervorragend. Ich habe dafür eine 'C#' Antwort mit 'DynamicMethod' [hier] erstellt (http://stackoverflow.com/a/43570334/147511). Danke vielmals. –

2

Ich weiß, dass int ein Werttyp ist, aber hier sollte es Hinweise auf das gleiche Objekt.

Ja, die Bezug auf die Methode übergeben sind die gleichen, aber sie sind geschachtelt (umgerechnet/Referenztyp Objekt) im ReferenceEquals Methode.

Deshalb gibt das Ergebnis Ihres Tests false zurück, da Sie Referenzen von zwei verschiedenen Objekten aufgrund von Boxen vergleichen.

Siehe: Object.ReferenceEquals Method

Wenn Werttypen zu vergleichen.Wenn objA und objB sind Werttypen, sind sie eingerahmt, bevor sie an die Methode ReferenceEquals übergeben werden. Das bedeutet, dass, wenn beide objA und objB stellen die gleichen Instanz einen Werttypen, die ReferenceEquals Methode dennoch false zurück

0

Die Verwirrung hier ist, weil im Gegensatz zu Zeigern (wie in *), „ref“ in C# ist kein Teil eines Typs, sondern ein Teil einer Methodensignatur. Es gilt für den Parameter und bedeutet "dies darf nicht kopiert werden". Es bedeutet nicht "dieses Argument hat Referenztyp".

Parameter, der von ref übergeben wird, anstatt einen neuen Speicherort anzugeben, ist stattdessen ein Alias ​​für einen vorhandenen Speicherort. Wie Alias ​​erstellt wird, ist technisch ein Implementierungsdetail. Meistens werden Aliase als verwaltete Referenzen implementiert, aber nicht immer. In einigen asynchronen verwandten Fällen könnte z. B. eine Referenz auf ein Array-Element intern als eine Kombination aus Array und Index dargestellt werden.

Im Wesentlichen werden Ihre a und b für C# immer noch als int-typisierte Variablen verstanden. Es ist legal und völlig normal, sie in jedem Ausdruck zu verwenden, der int-Werte wie a + b oder SomeMethod (a, b) verwendet, und in diesen Fällen werden die in a und b gespeicherten tatsächlichen int-Werte verwendet.

Es gibt wirklich kein Konzept einer "Referenz" als eine Entität, mit der Sie direkt in C# arbeiten können. Im Gegensatz zu Zeigern muss davon ausgegangen werden, dass die tatsächlichen Werte verwalteter Referenzen jederzeit oder sogar asynchron durch GC geändert werden können, sodass die Menge sinnvoller Szenarien für verwaltete Referenzen äußerst begrenzt wäre.