Testování rovnosti odkazů (Identita) (Průvodce programováním v C#)
Pro podporu porovnávání rovnosti odkazů ve vašich typech nemusíte implementovat žádnou vlastní logiku. Tato funkce je poskytována pro všechny typy statickou metodou Object.ReferenceEquals.
Následující příklad ukazuje, jak určit, zda dvě proměnné mají referenční rovnost, což znamená, že odkazují na stejný objekt v paměti.
Příklad také ukazuje, proč Object.ReferenceEquals vždy vrací false
pro typy hodnot. Důvodem je boxing, který vytváří samostatné instance objektů pro každý argument typu hodnoty. Kromě toho byste neměli používat ReferenceEquals k určení rovnosti řetězců.
Příklad
using System.Text;
namespace TestReferenceEquality
{
struct TestStruct
{
public int Num { get; private set; }
public string Name { get; private set; }
public TestStruct(int i, string s) : this()
{
Num = i;
Name = s;
}
}
class TestClass
{
public int Num { get; set; }
public string? Name { get; set; }
}
class Program
{
static void Main()
{
// Demonstrate reference equality with reference types.
#region ReferenceTypes
// Create two reference type instances that have identical values.
TestClass tcA = new TestClass() { Num = 1, Name = "New TestClass" };
TestClass tcB = new TestClass() { Num = 1, Name = "New TestClass" };
Console.WriteLine("ReferenceEquals(tcA, tcB) = {0}",
Object.ReferenceEquals(tcA, tcB)); // false
// After assignment, tcB and tcA refer to the same object.
// They now have reference equality.
tcB = tcA;
Console.WriteLine("After assignment: ReferenceEquals(tcA, tcB) = {0}",
Object.ReferenceEquals(tcA, tcB)); // true
// Changes made to tcA are reflected in tcB. Therefore, objects
// that have reference equality also have value equality.
tcA.Num = 42;
tcA.Name = "TestClass 42";
Console.WriteLine("tcB.Name = {0} tcB.Num: {1}", tcB.Name, tcB.Num);
#endregion
// Demonstrate that two value type instances never have reference equality.
#region ValueTypes
TestStruct tsC = new TestStruct( 1, "TestStruct 1");
// Value types are boxed into separate objects when passed to ReferenceEquals.
// Even if the same variable is used twice, boxing ensures they are different instances.
TestStruct tsD = tsC;
Console.WriteLine("After assignment: ReferenceEquals(tsC, tsD) = {0}",
Object.ReferenceEquals(tsC, tsD)); // false
#endregion
#region stringRefEquality
// Constant strings within the same assembly are always interned by the runtime.
// This means they are stored in the same location in memory. Therefore,
// the two strings have reference equality although no assignment takes place.
string strA = "Hello world!";
string strB = "Hello world!";
Console.WriteLine("ReferenceEquals(strA, strB) = {0}",
Object.ReferenceEquals(strA, strB)); // true
// After a new string is assigned to strA, strA and strB
// are no longer interned and no longer have reference equality.
strA = "Goodbye world!";
Console.WriteLine("strA = \"{0}\" strB = \"{1}\"", strA, strB);
Console.WriteLine("After strA changes, ReferenceEquals(strA, strB) = {0}",
Object.ReferenceEquals(strA, strB)); // false
// A string that is created at runtime cannot be interned.
StringBuilder sb = new StringBuilder("Hello world!");
string stringC = sb.ToString();
// False:
Console.WriteLine("ReferenceEquals(stringC, strB) = {0}",
Object.ReferenceEquals(stringC, strB));
// The string class overloads the == operator to perform an equality comparison.
Console.WriteLine("stringC == strB = {0}", stringC == strB); // true
#endregion
// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
}
/* Output:
ReferenceEquals(tcA, tcB) = False
After assignment: ReferenceEquals(tcA, tcB) = True
tcB.Name = TestClass 42 tcB.Num: 42
After assignment: ReferenceEquals(tsC, tsD) = False
ReferenceEquals(strA, strB) = True
strA = "Goodbye world!" strB = "Hello world!"
After strA changes, ReferenceEquals(strA, strB) = False
ReferenceEquals(stringC, strB) = False
stringC == strB = True
*/
Implementace Equals
v univerzální základní třídě System.Object také provádí kontrolu rovnosti odkazů, ale je nejlepší ji nepoužívat, protože pokud třída přepíše metodu, nemusí být výsledky takové, jaké očekáváte. Totéž platí pro operátory ==
a !=
. Pokud pracují s odkazovými typy, výchozím chováním ==
a !=
je provést kontrolu rovnosti odkazů. Odvozené třídy však mohou přetížit operátor k provedení kontroly rovnosti hodnot. Chcete-li minimalizovat potenciál chyby, je nejlepší vždy použít ReferenceEquals, pokud potřebujete určit, zda dva objekty mají rovnost odkazů.
Konstantní řetězce v rámci stejného sestavení jsou vždy provládnuty modulem runtime. To znamená, že je zachována pouze jedna instance každého jedinečného literálového řetězce. Runtime však nezaručuje, že se řetězce vytvořené za běhu internovány, ani nezaručuje, že jsou stejné konstantní řetězce v různých sestaveních internovány.
Poznámka
ReferenceEquals
vrací false
pro hodnotové typy z důvodu boxingu, protože každý argument je nezávisle zabalen do samostatného objektu.