Guide pratique pour tester l’égalité des références (Identity) (Guide de programmation C#)
Vous n’avez pas besoin d’implémenter une logique personnalisée pour prendre en charge les comparaisons d’égalité de référence dans vos types. Cette fonctionnalité est fournie pour tous les types par la méthode Object.ReferenceEquals statique.
L’exemple suivant montre comment déterminer si deux variables ont égalité de référence, ce qui signifie qu’elles font référence au même objet en mémoire.
L’exemple montre également pourquoi Object.ReferenceEquals retourne toujours false
pour les types valeur. Cela est dû au boxing, qui crée des instances d’objet distinctes pour chaque argument de type valeur. En outre, vous ne devez pas utiliser ReferenceEquals pour déterminer l’égalité des chaînes.
Exemple
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
*/
L’implémentation de Equals
dans la classe de base universelle System.Object effectue également une vérification de l’égalité des références, mais il est préférable de ne pas l’utiliser car, si une classe se produit pour remplacer la méthode, les résultats peuvent ne pas être ce que vous attendez. Il en va de même pour les opérateurs ==
et !=
. Lorsqu’ils fonctionnent sur des types de référence, le comportement par défaut de ==
et de !=
consiste à effectuer un contrôle d’égalité de référence. Toutefois, les classes dérivées peuvent surcharger l’opérateur pour effectuer une vérification de l’égalité des valeurs. Pour réduire le risque d’erreur, il est préférable d’utiliser toujours ReferenceEquals lorsque vous devez déterminer si deux objets ont une égalité de référence.
Les chaînes constantes au sein d’un même assembly sont toujours intégrées par le runtime. Autrement dit, une seule instance de chaque chaîne littérale unique est conservée. En revanche, le runtime ne garantit pas que les chaînes créées au moment de l’exécution sont intégrées, ni que deux chaînes constantes égales d’assemblys différents sont intégrées.
Remarque
ReferenceEquals
retourne false
pour les types valeur en raison du boxing, car chaque argument est boxé indépendamment dans un objet distinct.