Lorsqu’un object est converti dans une classe de base, comment se souvient-il de ce qu’il est réellement?

Ceci est une question de débutant, mais je suis intéressé à apprendre ce qui se passe ici. Ma question est la suivante: que se passe-t-il dans les coulisses lorsque vous rétablissez un object? Conserve-t-il une sorte de métadonnée sur ce qu’elle était à l’origine? Voici ce que je veux dire:

Supposons que j’ai une méthode appelée “ClockIn” qui accepte un paramètre de type “Employee”:

public static void ClockIn(Employee employee) { var manager = employee as Manager; if (manager != null) { manager.OpenSafe(); } } 

Supposons donc que Manager est une sous-classe du type Employee et qu’il utilise la méthode “OpenSafe”:

 public class Manager : Employee { public void OpenSafe() { ... } } 

La méthode “ClockIn”, si elle constate qu’un gestionnaire a été passé, appelle la méthode OpenSafe. Tel que:

 var bob = new Manager(); ClockIn(bob); 

Ici, j’ai passé une instance de type Manager à une méthode qui accepte la classe de base Employee. Je dois convertir l’instance de la méthode ClockIn en gestionnaire avant de pouvoir appeler OpenSafe.

La question est la suivante: y a-t-il des métadonnées qui rappellent que “bob” est un manager, même si je l’ai transféré en tant qu’employé? Comment le code sait-il qu’il peut effectivement être jeté sur un manager? Y a-t-il quelque chose dans le tas?

La première chose à retenir est que le casting ne change pas du tout l’object d’origine. Cela ne fait que changer votre vue de l’object via cette référence particulière. Plus d’une référence peut pointer sur le même object, il n’est donc pas raisonnable de le modifier sur une dissortingbution.

Ce que vous pourriez faire dans votre exemple est de faire de ClockIn() une méthode de la classe Employee . Puis, quand tu appelles

 bob.ClockIn(); 

alors bob saura de quel type il est réellement et appellera la méthode ClockIn() appropriée pour son type. C’est ce qu’on appelle l’envoi de méthode dynamic et n’est pas disponible pour static fonctions static , comme dans votre exemple.

Par exemple:

 public class Employee { public void ClockIn() { .... } } public class Manager: Employee { public void ClockIn() { // first, do what all Employees do when clocking in Employee.ClockIn(); // Next, do Manager specific actions OpenSafe(); } public void OpenSafe() { .... } } 

Les conversions ne modifient pas le type d’exécution d’un object.

L’appel de la méthode GetType sur un object renverra un object Type représentant son type d’exécution.

Le casting n’affecte pas l’object – il affecte la référence à l’object.

Tout ce que vous faites lorsque vous downcast un object, c’est d’indiquer au compilateur que cet object peut être référencé par une variable dérivée du type référençant actuellement l’object.

Le point clé est que la classe de l’object ne change jamais, vous ne faites que changer le type de la référence à l’object.

Le type réel d’object est toujours stocké en tant que lien vers son System.Type . En réalité, chaque object .NET possède un champ System.Type supplémentaire référençant son type réel.

En élargissant les réponses ci-dessus, il est utile de penser aux aspects publics d’une classe comme à un tableau élecsortingque. Des points de connexion fixes hors du monde s’attachent au fonctionnement interne de la classe. Si une classe hérite d’une autre classe, les objects de la nouvelle classe obtiennent un panneau étiqueté avec leur nouveau type de classe, en plus de disposer d’un panneau du type de classe de base. L’ajout d’une interface ajoute encore un autre panneau.

Si une propriété ou une méthode est déclarée remplaçable, l’arrière de la “connexion” du panneau pour cette méthode / propriété aura un bouchon détachable; sinon ce ne sera pas le cas. Supposons qu’une classe “Alpha” possède une méthode “foo” pouvant être remplacée et une fonction “bar” non remplaçable. Une classe dérivée “Bravo” remplace “foo” et les ombres “bar”. Les objects de la classe “Bravo” auront à la fois “Alpha.foo” et “Bravo.foo” reliés à la fonction “foo” de Bravo; ils auront “Alpha.Bar” relié à la fonction “Bar” d’Alpha et “Bravo.Bar” relié à la fonction “Bar” de Bravo.

Si un object “BravoInstance” de type “Bravo” est utilisé à un endroit où un “Bravo” est attendu, une référence à son “BravoInstance.Bar” obligera le système à regarder le panneau “Bravo” de l’object et à utiliser son “barre” connexion (câblé à Bravo.Bar). Si un tel cas est atsortingbué à un code qui attend un Alpha (parfaitement autorisé en raison d’un inheritance), une tentative d’access à ThatInstance.Bar se connectera à la connexion “Bar” du panneau “Alpha” (qui est elle-même connectée à Alpha.Bar). .

Parfois, l’observation est utile et appropriée, mais vous devez faire très attention à l’observation d’une méthode ou d’une propriété d’un object qui peut être transmise au code qui attend le type de base.