Show this page in english

Classes de base : System.Object

Publié le 3 Janvier 2007

1. Définition

1.1 Notions de base

System.Object est la classe de base fondamentale parmi toutes les classes du .NET Framework. Elle constitue la racine de la hiérarchie des types.

Lorsque vous créez une classe, celle hérite automatiquement et implicitement de la classe System.Object. Il est donc inutile de le préciser.

Ainsi, les deux exemples suivants sont équivalents :

class MyClass : System.Object
{ }
class MyClass { }

System.Object est une classe. Il s'agit donc d'un type référence. Un raccourci de System.Object est le mot clé object.

Ci-dessous, le contenu de cette classe :

[Serializable] [ComVisible(true)] [ClassInterface(ClassInterfaceType.AutoDual)] public class Object { // Methods [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public Object(); public virtual bool Equals(object obj); public static bool Equals(object objA, object objB); private void FieldGetter(string typeName, string fieldName, ref object val); private void FieldSetter(string typeName, string fieldName, object val); [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] protected override void Finalize(); private FieldInfo GetFieldInfo(string typeName, string fieldName); public virtual int GetHashCode(); [MethodImpl(MethodImplOptions.InternalCall)] public extern Type GetType(); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern bool InternalEquals(object objA, object objB); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int InternalGetHashCode(object obj); [MethodImpl(MethodImplOptions.InternalCall)] protected extern object MemberwiseClone(); [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static bool ReferenceEquals(object objA, object objB); public virtual string ToString(); }

1.2 Méthodes membres

Examinons en détails les méthodes membres public ou protected de cette classe (les private ne nous interessent pas puisque nous ne pourrons jamais nous en servir) :

public virtual bool Equals(object obj) : détermine si l'Object spécifié est égal à l'Object en cours.

Détails fournis plus loin.

public static bool Equals(object objA, object objB) : détermine si les instances de Object spécifiées sont considérées comme égales.

Détails fournis plus loin.

protected override void Finalize() : effectue des opérations de nettoyage avant qu'un objet soit automatiquement récupéré.

Cette méthode est automatiquement appelée lorsqu'un objet devient inaccessible, à moins qu'il ait été dispensé de finalisation par un appel à SuppressFinalize. Pendant l'arrêt d'un domaine d'application, Finalize est automatiquement appelé sur des objets qui ne sont pas dispensés de finalisation, même sur ceux qui sont toujours accessibles. Finalize n'est automatiquement appelé qu'une seule fois sur une instance donnée, à moins que l'objet soit réinscrit à l'aide d'un mécanisme tel que ReRegisterForFinalize et que GC.SuppressFinalize n'ait pas été appelé par la suite.

Toutes les implémentations de Finalize dans un type dérivé doivent appeler l'implémentation de Finalize de leur type de base. Il s'agit du seul cas où le code d'application est autorisé à appeler Finalize.

Si Finalize ou une substitution de Finalize lève une exception, et que le runtime n'est pas hébergé par une application qui substitue la stratégie par défaut, le runtime termine le processus et aucun bloc try-finally actif ni aucun finaliseur n'est exécuté. Ce comportement garantit l'intégrité du processus si le finaliseur ne peut pas libérer ou détruire des ressources.

Finalize possède également des restrictions.

public virtual int GetHashCode() : génère un nombre correspondant à la valeur de l'objet pour prendre en charge l'utilisation d'une table de hachage.

Cette méthode peut être substituée par une classe dérivée. Les classes de valeurs doivent substituer cette méthode pour fournir une fonction de hachage adaptée à la classe et qui garantit une meilleure distribution dans la table de hachage. Les classes qui peuvent être utilisées comme clés dans une table de hachage doivent également substituer cette méthode, car les objets qui sont utilisés comme clés dans une table de hachage sont requis pour générer leur propre code de hachage à l'aide de cette méthode. Toutefois, si les objets utilisés comme clés ne fournissent pas une implémentation utile de GetHashCode, vous pouvez fournir un autre fournisseur de code de hachage, basé sur l'interface System.Collections.IHashCodeProvider, lors de la construction de Hashtable.

L'implémentation par défaut de GetHashCode ne garantit ni son unicité ni sa cohérence ; par conséquent, vous ne devez pas l'utiliser en tant qu'identificateur d'objet unique à des fins de hachage. Les classes dérivées doivent substituer la méthode GetHashCode à l'aide d'une implémentation qui retourne un code de hachage unique. Pour obtenir de meilleurs résultats, basez le code de hachage sur la valeur d'un champ ou d'une propriété d'instance, et non sur un champ ou une propriété statique.

public extern Type GetType() : obtient le Type de l'instance en cours.

Retourne l'nstance de Type qui représente le type exact au moment de l'exécution de l'instance en cours.

protected extern object MemberwiseClone() : crée une copie partielle de l'Object en cours.

La méthode MemberwiseClone crée une copie superficielle en créant un nouvel objet puis en copiant les champs non statiques de l'objet actuel vers le nouvel objet. Si le champ est un type valeur, il est copié bit par bit. S'il s'agit d'un type référence, la référence est copiée, mais l'objet référencé ne l'est pas. Par conséquent, l'objet d'origine et son clone se réfèrent au même objet.

Prenez, par exemple, un objet appelé X qui référence des objets A et B. L'objet B référence à son tour l'objet C. Une copie partielle de X crée le nouvel objet X2 qui référence également les objets A et B. En revanche, une copie complète de X crée un nouvel objet X2 qui référence les nouveaux objets A2 et B2, lesquels sont des copies de A et B. B2 référence, à son tour C2, qui est une copie de C. Utilisez une classe qui implémente l'interface ICloneable pour exécuter une copie complète ou partielle d'un objet.

public static bool ReferenceEquals(object objA, object objB)

Détails fournis plus loin.

public virtual string ToString() : fabrique une chaîne de texte explicite qui décrit une instance de la classe.

Cette méthode retourne une chaîne explicite sensible à la culture. Par exemple, pour une instance de la classe Double ayant la valeur zéro, l'implémentation de Double.ToString peut retourner "0.00" ou "0,00" selon la culture de l'interface utilisateur en cours.

L'implémentation par défaut retourne le nom qualifié complet du type de Object.

2. Les comparaisons d'égalités

Vous aurez certainement remarqué que cette classe propose trois méthodes différentes pour comparer des objets entre eux. Bien que ces méthodes puissent paraître être identiques, elles font bien entendu des comparaisons différentes (sinon, rien ne sert d'en avoir trois).

2.1 La méthode virtuelle Equals

Il s'agit d'une méthode virtuelle. Pour que celle-ci soit utilisable vous devez la surchargez !

Cette méthode a pour vocation de comparer les valeurs des instances d'objets.

Si vous prévoyez d'utiliser vos instances de classes comme des clés de dictionnaires, vous devez surcharger cette méthode (afin d'éviter quelques désagréments).

Attention : Il ne faut jamais lever d'exceptions dans une méthode Equals. Cela peut causer des problèmes avec les classes directionnaires et d'autres classes .NET.

Exemple :

using System; public class Program { public static void Main() { Vecteur a = new Vecteur(2, 3); Vecteur b = new Vecteur(3, 2); Vecteur c = new Vecteur(3, 2); bool ab = a.Equals(b); //false bool bc = b.Equals(c); //true //== est un raccourci vers la méthode Equals car celle-ci a été surchargée bool ac = (a == c); //false } } //Un vecteur 2D public class Vecteur { private int x; private int y; public Vecteur(int x, int y) { this.x = x; this.y = y; } public override bool Equals(object obj) { if (obj == null || obj.GetType() != this.GetType()) return false; return (x == (obj as Vecteur).x && y == (obj as Vecteur).y); } }

Si vous surchargez cette méthode, vous êtes tenu de surcharger également GetHashCode (non fait ici), notamment si votre classe va être utilisée comem une clé de dictionnaire, sinon, le compilateur vous retourne un avertissement. Si vous êtes certains que la classe ne sera pas utilisée comme clé d'un dictionnaire, vous pouvez ne pas surcharger la fonction.

A la base, == compare des références mais si vous voulez que l'opérateur compare le contenu des classes plutôt que leurs instances (c'est le cas par exemple de la classe String dont on s'interesse davantage au contenu qu'aux instances), vous devez implémenter la méthode Equals.

Si vous surchargez l'opérateur ==, vous devez également surcharger la méthode Equals.

2.2 La méthode statique Equals

Cette méthode est très simple :

public static bool Equals(object objA, object objB)
{
if (objA == objB)
return true;

if ((objA != null) && (objB != null))
return objA.Equals(objB);

return false;
}

Donc, en surchargeant la méthode virtuelle Equals, vous surchargez par la même occasion la méthode statique Equals.

2.3 La méthode ReferenceEquals

Cette méthode fait exactement ce que son nom indique : elle compare les références de deux objets. Si les références pointent vers la même instance d'objet, elle retourne true, false dans les autres cas. Etant donnée que celle-ci est statique, il est impossible de la réimplémenter.

Exemple :

MyObject a = new MyObject(); MyObject b = new MyObject(); bool nn = ReferenceEquals(null, null); //true bool an = ReferenceEquals(a, null); //false bool ab = ReferenceEquals(a, b); //false bool aa = ReferenceEquals(a, a); //true

Remarque : Lorsque vous surchargez la méthode Equals, vous n'êtes pas obligé de surcharger également GetHashCode mais vous aurez un avertissement à la compilation.

Remarque : null est égal à null

L'exemple précédent n'est pas toujours vrai.

En effet, ReferenceEquals est implémenté de la manière suivante :

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static bool ReferenceEquals(object objA, object objB) { return (objA == objB); }

Par conséquent, si vous surchargez la méthode Equals de votre classe, celle-ci sera appellée immédiatement lors de l'appel de ReferenceEquals et la réponse dépendra de votre implémentation.

Par exemple, avec la classe System.String :

string s1 = "Coucou"; string s2 = "Coucou"; ReferenceEquals(s1, s2); //Renvoie true

De plus, si vous comparez un type valeur avec lui même à l'aide de cette méthode, celle-ci retourne false :

ReferenceEquals(v, v); //Renvoie false, v est un type valeur (comme int par exemple)

En effet, la méthode ReferenceEquals prend en paramètre deux object. Il se produit donc des opérations unboxing lorsque vous appellez cette méthode sur des types valeurs et même s'il s'agit de la même variable, les différents emboîtement de celui-ci mènent à des objets différents.

Sources :